class JpmGeo::Point

Point represents a latitude, longitude location

Constants

DEG_TO_RAD
MAX_LAT
MAX_LON
MIN_LAT
MIN_LON
PI
PI_2
RAD_TO_DEG

Attributes

lat[RW]
lon[RW]
radians[R]
radius[R]

Public Class Methods

from_degrees(lon:, lat:, radius: JpmGeo.radius) click to toggle source
# File lib/jpm_geo/point.rb, line 23
def from_degrees(lon:, lat:, radius: JpmGeo.radius)
  new(lon: lon, lat: lat, radius: radius, radians: false)
end
from_lonlat(lonlat, radius: JpmGeo.radius) click to toggle source
# File lib/jpm_geo/point.rb, line 19
def from_lonlat(lonlat, radius: JpmGeo.radius)
  from_degrees(lon: lonlat.lon, lat: lonlat.lat, radius: radius)
end
from_radians(lon:, lat:, radius: JpmGeo.radius, validate: true) click to toggle source
# File lib/jpm_geo/point.rb, line 27
def from_radians(lon:, lat:, radius: JpmGeo.radius, validate: true)
  new(lon: lon, lat: lat, radius: radius, radians: true, validate: validate)
end
new(lon:, lat:, radians: false, radius: JpmGeo.radius, validate: true) click to toggle source
# File lib/jpm_geo/point.rb, line 133
def initialize(lon:, lat:, radians: false, radius: JpmGeo.radius, validate: true)
  raise ArgumentError, "invalid radius" if radius <= 0

  @lon = lon
  @lat = lat
  @radius = radius
  @radians = radians
  raise ArgumentError, "invalid coordinates" if validate && !valid?
end
to_deg(radians) click to toggle source
# File lib/jpm_geo/point.rb, line 35
def to_deg(radians)
  radians * RAD_TO_DEG
end
to_rad(degrees) click to toggle source
# File lib/jpm_geo/point.rb, line 31
def to_rad(degrees)
  degrees * DEG_TO_RAD
end

Public Instance Methods

==(other) click to toggle source
# File lib/jpm_geo/point.rb, line 97
def ==(other)
  return false unless other.is_a?(JpmGeo::Point)

  point = radians ? other.to_radians : other.to_degrees
  point.lon == lon && point.lat == lat && point.radius == radius
end
bounding_coordinates(distance) click to toggle source

computes bounding coordinates all points that are within the great circle of the given distance from this Point. distance is in the same units as the radius argument.

# File lib/jpm_geo/point.rb, line 66
def bounding_coordinates(distance)
  raise ArgumentError, "invalid distance" if distance <= 0

  # angular distance in radians on a great circle
  raddist = distance / radius

  point = to_radians
  min = latlon(point.lat - raddist, 0)
  max = latlon(point.lat + raddist, 0)

  if min.lat > MIN_LAT && max.lat < MAX_LAT
    deltalon = Math.asin(Math.sin(raddist) / Math.cos(point.lat))
    min.lon = point.lon - deltalon
    max.lon = point.lon + deltalon
  else
    # a pole is within the distance
    min.lat = [min.lat, MIN_LAT].max
    max.lat = [max.lat, MAX_LAT].min
    min.lon = MIN_LON
    max.lon = MAX_LON
  end

  bounds = bounds_from_min_max(min, max)
  radians ? bounds : bounds.to_degrees
end
distance_to(point) click to toggle source
# File lib/jpm_geo/point.rb, line 57
def distance_to(point)
  raise ArgumentError, "JpmGeo::Point expected" unless point.is_a?(JpmGeo::Point)

  radians_distance_to(to_radians, point.to_radians)
end
to_degrees() click to toggle source
# File lib/jpm_geo/point.rb, line 46
def to_degrees
  return self unless radians

  Point.from_degrees(lon: Point.to_deg(lon), lat: Point.to_deg(lat), radius: radius)
end
to_radians() click to toggle source
# File lib/jpm_geo/point.rb, line 40
def to_radians
  return self if radians

  Point.from_radians(lon: Point.to_rad(lon), lat: Point.to_rad(lat), radius: radius)
end
to_s() click to toggle source
# File lib/jpm_geo/point.rb, line 92
def to_s
  units = radians ? " rad" : "°"
  "JpmGeo::Point(#{lat}#{units}, #{lon}#{units})"
end
valid?() click to toggle source
# File lib/jpm_geo/point.rb, line 52
def valid?
  point = to_radians
  point.lat >= MIN_LAT && point.lat <= MAX_LAT && point.lon >= MIN_LON && point.lon <= MAX_LON
end

Private Instance Methods

bounds_from_min_max(min, max) click to toggle source
# File lib/jpm_geo/point.rb, line 112
def bounds_from_min_max(min, max)
  # http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#PolesAnd180thMeridian
  # when the 180th meridian is with the query circle, return two sets of bounding coordinates.
  if min.lon < MIN_LON
    # (latmin, lonmin + 2PI), (latmax, PI) and (latmin, -PI), (latmax, lonmax)
    Bounds.from_points(latlon(min.lat, min.lon + PI_2), latlon(max.lat, PI),
                       latlon(min.lat, -PI), latlon(max.lat, max.lon))
  elsif max.lon > MAX_LON
    # (latmin, lonmin), (latmax, PI) and (latmin, -PI), (latmax, lonmax - 2PI).
    Bounds.from_points(latlon(min.lat, min.lon), latlon(max.lat, PI),
                       latlon(min.lat, -PI), latlon(max.lat, max.lon - PI_2))
  else
    # does not cross 180th meridian
    Bounds.from_points(min, max)
  end
end
latlon(lat, lon) click to toggle source
# File lib/jpm_geo/point.rb, line 129
def latlon(lat, lon)
  Point.from_radians(lat: lat, lon: lon, radius: radius, validate: false)
end
radians_distance_to(point1, point2) click to toggle source
# File lib/jpm_geo/point.rb, line 106
def radians_distance_to(point1, point2)
  Math.acos(Math.sin(point1.lat) * Math.sin(point2.lat) +
            Math.cos(point1.lat) * Math.cos(point2.lat) *
            Math.cos(point1.lon - point2.lon)) * radius
end