Geographic coordinate operations with Haversine/Vincenty distance, geohash, rhumb lines, and bounding box
gem install philiprehberger-geo_pointGeographic coordinate operations with Haversine/Vincenty distance, geohash, rhumb lines, and bounding box
Add to your Gemfile:
gem "philiprehberger-geo_point"
Or install directly:
gem install philiprehberger-geo_point
require "philiprehberger/geo_point"
nyc = Philiprehberger::GeoPoint.point(40.7128, -74.0060)
london = Philiprehberger::GeoPoint.point(51.5074, -0.1278)
nyc.distance_to(london) # => ~5570.0 (km)
nyc.distance_to(london, unit: :km) # => ~5570.0
nyc.distance_to(london, unit: :mi) # => ~3461.0
nyc.distance_to(london, unit: :m) # => ~5570000.0
nyc.distance_to(london, unit: :nm) # => ~3007.0
nyc.distance_to(london, method: :vincenty) # => ~5585.0 (ellipsoid-accurate)
nyc.distance_to(london, method: :haversine) # => ~5570.0 (spherical, default)
nyc.bearing_to(london) # => ~51.2 (degrees, 0-360)
mid = nyc.midpoint(london)
mid.lat # => ~52.5
mid.lon # => ~-36.0
dest = nyc.destination(51.2, 5570) # bearing, distance in km
dest.lat # => ~51.5
dest.lon # => ~-0.1
# Distance in meters, bearing in degrees (0 = north, clockwise)
dest = nyc.destination(distance: 5_570_000, bearing: 51.2)
dest.lat # => ~51.5
dest.lon # => ~-0.1
nyc = Philiprehberger::GeoPoint.point(40.7128, -74.0060)
london = Philiprehberger::GeoPoint.point(51.5074, -0.1278)
nyc.interpolate(london, 0.0) # => nyc
nyc.interpolate(london, 0.5) # => midpoint along the great circle
nyc.interpolate(london, 1.0) # => london
nyc.to_geohash # => "dr5ru6j2c62g" (precision 12)
nyc.to_geohash(precision: 6) # => "dr5ru6"
point = Philiprehberger::GeoPoint.from_geohash("dr5ru6j2c62g")
point.lat # => ~40.7128
point.lon # => ~-74.0060
point = Philiprehberger::GeoPoint.point(1, 5)
path_start = Philiprehberger::GeoPoint.point(0, 0)
path_end = Philiprehberger::GeoPoint.point(0, 10)
point.cross_track_distance(path_start, path_end) # => ~111.2 (km, positive = right of path)
nyc.rhumb_distance_to(london) # => ~5800.0 (km, constant-bearing route)
nyc.rhumb_bearing_to(london) # => ~79.3 (degrees, constant bearing)
triangle = [
Philiprehberger::GeoPoint.point(0, 0),
Philiprehberger::GeoPoint.point(0, 10),
Philiprehberger::GeoPoint.point(10, 5)
]
inside = Philiprehberger::GeoPoint.point(3, 5)
outside = Philiprehberger::GeoPoint.point(20, 20)
Philiprehberger::GeoPoint.inside_polygon?(inside, triangle) # => true
Philiprehberger::GeoPoint.inside_polygon?(outside, triangle) # => false
origin = Philiprehberger::GeoPoint.point(40.7128, -74.0060)
nearest = Philiprehberger::GeoPoint.nearest(origin, points)
nearby = Philiprehberger::GeoPoint.within_radius(origin, points, 50)
clusters = Philiprehberger::GeoPoint.cluster(points, radius_km: 10)
clusters.each { |group| puts "Cluster of #{group.length} points" }
box = Philiprehberger::GeoPoint::BoundingBox.around(nyc, 50)
box.contains?(nyc) # => true
box.contains?(london) # => false
box = Philiprehberger::GeoPoint::BoundingBox.around(point, 50)
box.area_km2 # => 10000.0 (approximate km²)
nyc.to_dms # => "40°42'46\"N 74°0'22\"W"
nyc.to_a # => [40.7128, -74.0060]
nyc.to_h # => {lat: 40.7128, lon: -74.0060}
# Canonical degree-minute-second symbols
Philiprehberger::GeoPoint::Point.from_dms("40°45'30\"N", "73°59'15\"W")
# => #<Point lat=40.758... lon=-73.987...>
# Space-separated, plain decimal-degree, decimal seconds — all accepted
Philiprehberger::GeoPoint::Point.from_dms("40 45 30 N", "73 59 15 W")
Philiprehberger::GeoPoint::Point.from_dms("40.7128", "-74.0060")
GeoPoint| Method | Description |
|---|---|
.point(lat, lon) | Create a new Point instance |
.from_geohash(hash) | Decode a geohash string to a Point at the center of the cell |
.inside_polygon?(point, vertices) | Check if a point is inside a polygon using ray-casting |
.nearest(origin, points) | Find closest point from array |
.within_radius(origin, points, radius_km) | Filter points by distance |
.cluster(points, radius_km:) | Group nearby points into clusters |
GeoPoint::Point| Method | Description |
|---|---|
.new(lat, lon) | Create point with coordinate validation (-90..90, -180..180) |
.from_dms(lat, lon) | Parse DMS strings ("40°45'30\"N", "40 45 30 N", decimal-degree, etc.) into a Point |
#distance_to(other, unit: :km, method: :haversine) | Distance via Haversine or Vincenty (:km, :mi, :m, :nm) |
#bearing_to(other) | Initial bearing in degrees (0-360) |
#midpoint(other) | Geographic midpoint between two points |
#interpolate(other, fraction) | Point at the given fraction along the great-circle path (0.0 → self, 1.0 → other) |
#destination(bearing, distance, unit: :km) | Point at given bearing and distance |
#destination(distance:, bearing:) | Forward geodesic: destination from distance (meters) and bearing (degrees) |
#to_geohash(precision: 12) | Encode point as a geohash string (precision 1-12) |
#cross_track_distance(path_start, path_end, unit: :km) | Perpendicular distance to great circle path |
#rhumb_distance_to(other, unit: :km) | Rhumb line (constant-bearing) distance |
#rhumb_bearing_to(other) | Rhumb line bearing in degrees (0-360) |
#to_dms | Format as degrees, minutes, seconds string |
#to_a | Return [lat, lon] array |
#to_h | Return {lat:, lon:} hash |
GeoPoint::BoundingBox| Method | Description |
|---|---|
.around(point, radius, unit: :km) | Create bounding box around a point |
#contains?(point) | Check if point is within the box |
#to_a | Return [min_lat, max_lat, min_lon, max_lon] array |
#area_km2 | Approximate surface area in km² |
#to_h | Return hash with all bounds |
bundle install
bundle exec rspec
bundle exec rubocop
If you find this project useful: