Last active
April 26, 2016 19:40
-
-
Save mrcsparker/67d2ea3dc30e98bdbe6ecaf696042706 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# <p>Represents a point on the surface of a sphere. (The Earth is almost | |
# spherical.)</p> | |
# | |
# <p>To create an instance, call one of the static methods fromDegrees() or | |
# fromRadians().</p> | |
# | |
# <p>This code was originally published at | |
# <a href="http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates#Java"> | |
# http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates#Java</a>.</p> | |
# | |
# @author Jan Philip Matuschek | |
# @version 22 September 2010 | |
# | |
# Ruby version | |
# @author Chris Parker | |
# | |
class Numeric | |
def degrees | |
self * 180 / Math::PI | |
end | |
def radians | |
self * Math::PI / 180 | |
end | |
end | |
class GeoLocation | |
attr_accessor :radian_latitude # latitude in radians | |
attr_accessor :radian_longitude # longitude in radians | |
attr_accessor :degree_latitude # latitude in degrees | |
attr_accessor :degree_longitude # longitude in degrees | |
MIN_LAT = -90.radians # -PI/2 | |
MAX_LAT = 90 # PI/2 | |
MIN_LON = -180.radians # -PI | |
MAX_LON = 180.radians # PI | |
def initializer | |
end | |
## | |
# @param latitude the latitude, in degrees. | |
# @param longitude the longitude, in degrees. | |
def from_degrees(latitude, longitude) | |
result = GeoLocation.new | |
result.radian_latitude = latitude.radians | |
result.radian_longitude = longitude.radians | |
result.degree_latitude = latitude | |
result.degree_longitude = longitude | |
result.check_bounds() | |
result | |
end | |
## | |
# @param latitude the latitude, in radians. | |
# @param longitude the longitude, in radians. | |
def from_radians(latitude, longitude) | |
result = GeoLocation.new | |
result.radian_latitude = latitude | |
result.radian_longitude = longitude | |
result.degree_latitude = latitude.degrees | |
result.degree_longitude = longitude.degrees | |
result.check_bounds | |
result | |
end | |
def check_bounds | |
if (@radian_latitude < MIN_LAT || @radian_latitude > MAX_LAT || | |
@radian_longitude < MIN_LON || @radian_longitude > MAX_LON) | |
raise Exception.new("Illegal Argument") | |
end | |
end | |
# @return the latitude, in degrees. | |
def get_latitude_in_degrees | |
@degree_latitude | |
end | |
# @return the longitude, in degrees. | |
def get_longitude_in_degrees | |
@degree_longitude | |
end | |
# @return the latitude, in radians. | |
def get_latitude_in_radians | |
@radian_latitude | |
end | |
# @return the longitude, in radians. | |
def get_longitude_in_radians | |
@radian_longitude | |
end | |
def to_s | |
"(" + @degree_latitude + "\u00B0, " + @degree_longitude + "\u00B0) = (" + | |
@radian_latitude + " rad, " + @radian_longitude + " rad)" | |
end | |
## | |
# Computes the great circle distance between this GeoLocation instance | |
# and the location argument. | |
# @param radius the radius of the sphere, e.g. the average radius for a | |
# spherical approximation of the figure of the Earth is approximately | |
# 6371.01 kilometers. | |
# @return the distance, measured in the same unit as the radius | |
# argument. | |
def distance_to(location, radius) | |
Math.acos( | |
Math.sin(@radian_latitude) * Math.sin(location.radian_latitude) + | |
Math.cos(@radian_latitude) * Math.cos(location.radian_latitude) * | |
Math.cos(@radian_longitude - location.radian_longitude) | |
) * radius | |
end | |
## | |
# <p>Computes the bounding coordinates of all points on the surface | |
# of a sphere that have a great circle distance to the point represented | |
# by this GeoLocation instance that is less or equal to the distance | |
# argument.</p> | |
# <p>For more information about the formulae used in this method visit | |
# <a href="http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates"> | |
# http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates</a>.</p> | |
# @param distance the distance from the point represented by this | |
# GeoLocation instance. Must be measured in the same unit as the radius | |
# argument. | |
# @param radius the radius of the sphere, e.g. the average radius for a | |
# spherical approximation of the figure of the Earth is approximately | |
# 6371.01 kilometers. | |
# @return an array of two GeoLocation objects such that:<ul> | |
# <li>The latitude of any point within the specified distance is greater | |
# or equal to the latitude of the first array element and smaller or | |
# equal to the latitude of the second array element.</li> | |
# <li>If the longitude of the first array element is smaller or equal to | |
# the longitude of the second element, then | |
# the longitude of any point within the specified distance is greater | |
# or equal to the longitude of the first array element and smaller or | |
# equal to the longitude of the second array element.</li> | |
# <li>If the longitude of the first array element is greater than the | |
# longitude of the second element (this is the case if the 180th | |
# meridian is within the distance), then | |
# the longitude of any point within the specified distance is greater | |
# or equal to the longitude of the first array element | |
# <strong>or</strong> smaller or equal to the longitude of the second | |
# array element.</li> | |
# </ul> | |
def bounding_coordinates(distance, radius) | |
if (radius < 0 || distance < 0) | |
raise Exception.new("Illegal Argument") | |
end | |
# angular distance in radians on a great circle | |
radian_distance = distance / radius | |
minimum_latitude = @radian_latitude - radian_distance | |
maximum_latitude = @radian_latitude + radian_distance | |
minimum_longitude = Float(0) | |
maximum_longitude = Float(0) | |
if (minimum_latitude > MIN_LAT && maximum_latitude < MAX_LAT) | |
delta_longitude = Math.asin(Math.sin(radian_distance) / Math.cos(@radian_latitude)) | |
minimum_longitude = @radian_longitude - delta_longitude | |
if (minimum_longitude < MIN_LON) | |
minimum_longitude += 2 * Math::PI | |
end | |
maximum_longitude = @radian_longitude + delta_longitude | |
if (maximum_longitude > MAX_LON) | |
maximum_longitude -= 2 * Math::PI | |
end | |
else | |
# a pole is within the distance | |
minimum_latitude = Math.max(minimum_latitude, MIN_LAT) | |
maximum_latitude = Math.min(maximum_latitude, MAX_LAT) | |
minimum_longitude = MIN_LON | |
maximum_longitude = MAX_LON | |
end | |
[ | |
from_radians(minimum_latitude, minimum_longitude), | |
from_radians(maximum_latitude, maximum_longitude) | |
] | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment