Skip to content

Instantly share code, notes, and snippets.

@mrcsparker
Last active April 26, 2016 19:40
Show Gist options
  • Save mrcsparker/67d2ea3dc30e98bdbe6ecaf696042706 to your computer and use it in GitHub Desktop.
Save mrcsparker/67d2ea3dc30e98bdbe6ecaf696042706 to your computer and use it in GitHub Desktop.
##
# <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