lib/mongoid/geospatial/fields/point.rb in mongoid-geospatial-4.0.0 vs lib/mongoid/geospatial/fields/point.rb in mongoid-geospatial-4.0.1

- old
+ new

@@ -4,16 +4,18 @@ # class Point include Enumerable attr_reader :x, :y - def initialize(x, y) - @x, @y = x, y + def initialize(x, y, z = nil) + @x, @y, @z = x, y, z end # Object -> Database # Let's store NilClass if we are invalid. + # + # @return (Array) def mongoize return nil unless x && y [x, y] end alias_method :to_a, :mongoize @@ -25,33 +27,49 @@ def each yield x yield y end - - def to_s - "#{x}, #{y}" - end - + # + # Point representation as a Hash + # + # @return (Hash) def to_hsh(xl = :x, yl = :y) { xl => x, yl => y } end alias_method :to_hash, :to_hsh + # Helper for [self, radius] def radius(r = 1) [mongoize, r] end + # Helper for [self, radius / earth radius] def radius_sphere(r = 1, unit = :km) radius r.to_f / Mongoid::Geospatial.earth_radius[unit] end + # + # Am I valid? + # + # Validates that x & y are `Numeric` + # def valid? x && y && x.is_a?(Numeric) && y.is_a?(Numeric) end # + # Point definition as string + # + # "x, y" + # + # @return [String] Point as comma separated String + def to_s + "#{x}, #{y}" + 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 @@ -76,66 +94,98 @@ # c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)) # r * c # end class << self + # + # Database -> Object + # Get it back + def demongoize(obj) + obj && Point.new(*obj) + end + + # + # Object -> Database + # Send it to MongoDB + def mongoize(obj) + case obj + when Point then obj.mongoize + when String then from_string(obj) + when Array then from_array(obj) + when Hash then from_hash(obj) + when NilClass then nil + else + return obj.to_xy if obj.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(obj) + obj.respond_to?(:x) ? obj.mongoize : obj + end + + private + + # + # Sanitize a `Point` from a `String` + # # Makes life easier: - # "" -> nil + # "" -> [] + # "1, 2" -> [1.0, 2.0] + # "1.1 2.2" -> [1.1, 2.2] + # + # @return (Array) + # def from_string(str) return nil if str.empty? str.split(/,|\s/).reject(&:empty?).map(&:to_f) end + # + # Sanitize a `Point` from an `Array` + # # Also makes life easier: - # [] -> nil + # [] -> [] + # [1,2] -> [1.0, 2.0] + # + # @return (Array) + # def from_array(ary) return nil if ary.empty? - ary[0..1].map(&:to_f) + ary.flatten[0..1].map(&:to_f) end - # Throw error on wrong hash, just for a change. + # + # Sanitize a `Point` from a `Hash` + # + # Uses Mongoid::Geospatial.lat_symbols & lng_symbols + # + # Also makes life easier: + # {x: 1.0, y: 2.0} -> [1.0, 2.0] + # {lat: 1.0, lon: 2.0} -> [1.0, 2.0] + # {lat: 1.0, long: 2.0} -> [1.0, 2.0] + # + # Throws error if hash has less than 2 items. + # + # @return (Array) + # 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 + fail "Hash must contain #{Mongoid::Geospatial.lat_symbols.inspect}" 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(hsh.keys[0]) - hsh.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 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 + fail "Hash must contain #{Mongoid::Geospatial.lng_symbols.inspect}" end end # << self end # Point end # Geospatial end # Mongoid