lib/quadtree/point.rb in quadtree-1.0.7 vs lib/quadtree/point.rb in quadtree-1.0.8
- old
+ new
@@ -1,107 +1,161 @@
-module Quadtree
- # Simple coordinate object to represent points in some space.
- class Point
-
- # The X coordinate of this instance.
- # @return [Float, Integer] X coordinate.
- attr_accessor :x
-
- # The Y coordinate of this instance.
- # @return [Float, Integer] Y coordinate.
- attr_accessor :y
-
- # Optional payload attached to this instance.
- # @return [Object] payload attached to this instance.
- attr_accessor :data
-
- # @param x [Float, Integer] X coordinate.
- # @param y [Float, Integer] Y coordinate.
- # @param data [Object] payload payload attached to this instance
- # (optional).
- # @raise [UnknownTypeError] if one or more input parameters (+x+ and +y+)
- # has the wrong type.
- def initialize(x, y, data=nil)
-
- self.x = get_typed_numeric(x)
- self.y = get_typed_numeric(y)
-
- self.data = data unless data.nil?
- end
-
- # This will calculate distance to another {Point}, given that they are
- # both in the same 2D space.
- #
- # @param other [Point] the other {Point}.
- # @return [Float] the distance to the other {Point}.
- def distance_to(other)
- Math.sqrt((other.x - self.x) ** 2 + (other.y - self.y) ** 2)
- end
-
- # This will calculate distance to another {Point} using the Haversine
- # formula. This means that it will treat {#x} as longitude and {#y} as
- # latitude!
- #
- # a = sin²(Δφ/2) + cos φ_1 ⋅ cos φ_2 ⋅ sin²(Δλ/2)
- #
- # c = 2 ⋅ atan2( √a, √(1−a) )
- #
- # d = R ⋅ c
- #
- # where φ is latitude, λ is longitude, R is earth’s radius (mean
- # radius = 6 371 km);
- # note that angles need to be in radians to pass to trig functions!
- #
- # @param other [Point] the other {Point}.
- # @return [Float] the distance, in meters, to the other {Point}.
- def haversine_distance_to(other)
- # earth's radius
- r = 6371 * 1000.0
- # coverting degrees to radians
- lat1 = self.y * (Math::PI / 180.0)
- lat2 = other.y * (Math::PI / 180.0)
- dlat = (other.y - self.y) * (Math::PI / 180.0)
- dlon = (other.x - self.x) * (Math::PI / 180.0)
-
- # a = sin²(Δφ/2) + cos φ_1 ⋅ cos φ_2 ⋅ sin²(Δλ/2)
- a = Math.sin(dlat / 2.0) * Math.sin(dlat / 2.0) +
- Math.cos(lat1) * Math.cos(lat2) *
- Math.sin(dlon / 2.0) * Math.sin(dlon / 2.0)
- # c = 2 ⋅ atan2( √a, √(1−a) )
- c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
- # d = R ⋅ c
- return r * c
- end
-
- private
-
- def get_typed_numeric(any_input)
- typed_output = nil
- # Try integer first since float will parse integers too
- return get_integer(any_input) unless get_integer(any_input).nil?
- # Try Float next
- return get_float(any_input) unless get_float(any_input).nil?
-
- raise UnknownTypeError.new "Unknown type for parameter: #{any_input.class}"
-
- #typed_output
- end
-
- def get_integer(any_input)
- return Integer(any_input) if any_input.is_a? String
- return any_input if any_input.is_a? Integer
-
- nil
- rescue
- nil
- end
-
- def get_float(any_input)
- return Float(any_input) if any_input.is_a? String
- return any_input if any_input.is_a? Float
-
- nil
- rescue
- nil
- end
- end
-end
+# frozen_string_literal: true
+
+module Quadtree
+ # Simple coordinate object to represent points in some space.
+ class Point
+ # The X coordinate of this instance.
+ # @return [Float, Integer] X coordinate.
+ attr_accessor :x
+
+ # The Y coordinate of this instance.
+ # @return [Float, Integer] Y coordinate.
+ attr_accessor :y
+
+ # Optional payload attached to this instance.
+ # @return [Object] payload attached to this instance.
+ attr_accessor :data
+
+ # @param x [Float, Integer] X coordinate.
+ # @param y [Float, Integer] Y coordinate.
+ # @param data [Object] payload payload attached to this instance
+ # (optional).
+ # @raise [UnknownTypeError] if one or more input parameters (+x+ and +y+)
+ # has the wrong type.
+ def initialize(x, y, data = nil)
+ self.x = get_typed_numeric(x)
+ self.y = get_typed_numeric(y)
+
+ self.data = data unless data.nil?
+ end
+
+ #
+ # Create a Hash for this {Point}.
+ #
+ # @return [Hash] Hash representation of this {Point}.
+ #
+ def to_h
+ {
+ 'x': x,
+ 'y': y,
+ 'data': process_data(data)
+ }
+ end
+
+ #
+ # Create a Hash for this {Point}.
+ #
+ # @return [Hash] Hash representation of this {Point}.
+ #
+ def to_hash
+ to_h
+ end
+
+ #
+ # Create a JSON String representation of this {Point}.
+ #
+ # @return [String] JSON String of this {Point}.
+ #
+ def to_json(*_args)
+ require 'json'
+ to_h.to_json
+ end
+
+ #
+ # Create a String for this {Point}.
+ #
+ # @return [String] String representation of this {Point}.
+ #
+ def to_s
+ to_h.to_s
+ end
+
+ #
+ # Construct a {Quadtree::Point} from a JSON String.
+ #
+ # @param [String] json_data input JSON String.
+ #
+ # @return [Quadtree::Point] the {Quadtree::Point} contained in the JSON String.
+ #
+ def self.from_json(json_data)
+ new(json_data['x'], json_data['y'], json_data['data'])
+ end
+
+ # This will calculate distance to another {Point}, given that they are
+ # both in the same 2D space.
+ #
+ # @param other [Point] the other {Point}.
+ # @return [Float] the distance to the other {Point}.
+ def distance_to(other)
+ Math.sqrt((other.x - x)**2 + (other.y - y)**2)
+ end
+
+ # This will calculate distance to another {Point} using the Haversine
+ # formula. This means that it will treat {#x} as longitude and {#y} as
+ # latitude!
+ #
+ # a = sin²(Δφ/2) + cos φ_1 ⋅ cos φ_2 ⋅ sin²(Δλ/2)
+ #
+ # c = 2 ⋅ atan2( √a, √(1−a) )
+ #
+ # d = R ⋅ c
+ #
+ # where φ is latitude, λ is longitude, R is earth’s radius (mean
+ # radius = 6 371 km);
+ # note that angles need to be in radians to pass to trig functions!
+ #
+ # @param other [Point] the other {Point}.
+ # @return [Float] the distance, in meters, to the other {Point}.
+ def haversine_distance_to(other)
+ lat1 = y * (Math::PI / 180.0)
+ lat2 = other.y * (Math::PI / 180.0)
+ dlat = (other.y - y) * (Math::PI / 180.0)
+ dlon = (other.x - x) * (Math::PI / 180.0)
+
+ # a = sin²(Δφ/2) + cos φ_1 ⋅ cos φ_2 ⋅ sin²(Δλ/2)
+ a = calculate_haversine_a(lat1, lat2, dlat, dlon)
+ # c = 2 ⋅ atan2( √a, √(1−a) )
+ c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
+ # d = R ⋅ c
+ 6371 * 1000.0 * c
+ end
+
+ private
+
+ def calculate_haversine_a(lat1, lat2, dlat, dlon)
+ Math.sin(dlat / 2.0) * Math.sin(dlat / 2.0) +
+ Math.cos(lat1) * Math.cos(lat2) *
+ Math.sin(dlon / 2.0) * Math.sin(dlon / 2.0)
+ end
+
+ def process_data(data)
+ data.nil? || data.is_a?(Array) || data.is_a?(String) || data.is_a?(Numeric) ? data : data.to_h
+ end
+
+ def get_typed_numeric(any_input)
+ # Try integer first since float will parse integers too
+ return get_integer(any_input) unless get_integer(any_input).nil?
+ # Try Float next
+ return get_float(any_input) unless get_float(any_input).nil?
+
+ raise UnknownTypeError, "Unknown type for parameter: #{any_input.class}"
+ end
+
+ def get_integer(any_input)
+ return Integer(any_input) if any_input.is_a? String
+ return any_input if any_input.is_a? Integer
+
+ nil
+ rescue StandardError
+ nil
+ end
+
+ def get_float(any_input)
+ return Float(any_input) if any_input.is_a? String
+ return any_input if any_input.is_a? Float
+
+ nil
+ rescue StandardError
+ nil
+ end
+ end
+end