
Geodetic 0.8.0 is now available. This release brings production-tested GIS coordinate conversion to Ruby, with 20 years of battle-tested algorithms from commercial applications now shared freely.
Geodetic is a Ruby gem for precise conversion between 18 geodetic coordinate systems and geographic calculations. Every system converts bidirectionally to every other system with no third-party dependencies beyond Ruby itself (optional C library acceleration available).
Install:
gem install geodetic
Or add to your Gemfile:
gem "geodetic"
Convert between any of these coordinate systems with full precision:
require "geodetic"
include Geodetic
seattle = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
# Convert to any other system
utm = seattle.to_utm
web_merc = seattle.to_web_mercator
mgrs = seattle.to_mgrs
h3 = seattle.to_h3
# Convert back — all systems are fully bidirectional
seattle_again = utm.to_lla
All conversions use the same ellipsoid model, datum transformations, and high-precision math. You don’t choose a conversion method — the system knows how to reach any other system.
Distance calculations return Distance objects that track units automatically and convert on demand:
portland = Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
distance = seattle.distance_to(portland)
distance.meters # => 235385.71
distance.to_km.to_f # => 235.39
distance.to_mi.to_f # => 146.26
# Bearing returns a Bearing object
bearing = seattle.bearing_to(portland)
bearing.degrees # => 186.25
bearing.to_compass # => "S"
bearing.to_compass(points: 8) # => "SSW"
Distances use Vincenty’s algorithm on the WGS84 ellipsoid for great-circle accuracy.
Build and work with geographic features:
# Create paths
route = Path.new(coordinates: [seattle, portland, sf, la])
route.total_distance.to_km
route.bounds # => BoundingBox
# Point-in-polygon testing
polygon = Areas::Polygon.new(boundary: [p1, p2, p3, p4])
polygon.includes?(test_point)
# Segments with projection
segment = Segment.new(seattle, portland)
closest_point, distance = segment.project(off_path_point)
# Areas: Circle, Rectangle, Triangle, Pentagon, Hexagon, Octagon
circle = Areas::Circle.new(centroid: seattle, radius: 1000.0)
circle.includes?(test_point)
Convert between ellipsoidal heights (WGS84) and orthometric heights (mean sea level) using EGM96, EGM2008, GEOID18, or GEOID12B models:
lla = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
lla.geoid_height # => geoid undulation at this location
lla.orthometric_height # => height above mean sea level
lla.convert_height_datum("HAE", "NAVD88")
Render Geodetic objects directly onto maps with a pluggable adapter pattern. New in 0.8.0:
require "geodetic"
require "libgd-gis"
map = Geodetic::Map::LibGdGis.new(
bbox: Geodetic::Areas::BoundingBox.new(nw: nw, se: se),
width: 800, height: 600
)
# Add any geometry with optional styles
map.add(seattle, color: "red", label: "Seattle")
map.add(route, color: "blue", width: 3)
map.add(polygon, fill_color: "green")
# Render to PNG
map.render("output.png")
Supports points, lines, polygons with colors, strokes, labels, icons, and custom fonts. The adapter pattern lets other backends (web maps, 3D visualizations) be added without changing Geodetic itself.
For large-scale polygon operations, Geodetic automatically uses libgeos_c when installed. The library detects GEOS at runtime and falls back to pure Ruby if not available.
# These work either way — GEOS accelerates when available
polygon.includes?(point) # O(log n) with GEOS vs O(n) with Ruby
path.intersects?(other_path) # Much faster for large paths
Point-in-polygon and intersection tests work with or without GEOS — GEOS just makes them faster. Some operations like union are GEOS-only. Set GEODETIC_GEOS_DISABLE=1 to force pure Ruby even when GEOS is installed.
Export and import standard formats used by PostGIS, GIS tools, and web services:
# GeoJSON — build FeatureCollections
geojson = Geodetic::GeoJSON.new
geojson << seattle
geojson << feature
geojson.save("map.geojson", pretty: true)
# WKT — PostGIS compatible
polygon.to_wkt # => "POLYGON((-122.35 47.62, ...))"
polygon.to_wkt(srid: 4326) # => "SRID=4326;POLYGON(...)"
# WKB — binary format for compact storage
binary = polygon.to_wkb
hex = polygon.to_wkb_hex(srid: 4326)
Geodetic::WKB.save_hex!("shapes.wkb.hex", seattle, segment, polygon)
Distance and Bearing are immutable value types with full arithmetic:
d1 = Geodetic::Distance.km(5)
d2 = Geodetic::Distance.mi(3)
(d1 + d2).meters # => 9828.032
(d1 * 2).meters # => 10000.0
b1 = Geodetic::Bearing.new(90)
b1 - Geodetic::Bearing.new(45) # => 45.0 (Float)
The examples/ directory contains 14 progressive scripts demonstrating every feature:
ruby -Ilib examples/01_basic_conversions.rb
ruby -Ilib examples/02_all_coordinate_systems.rb
ruby -Ilib examples/03_distance_calculations.rb
ruby -Ilib examples/04_bearing_calculations.rb
ruby -Ilib examples/05_map_rendering/demo.rb
ruby -Ilib examples/06_path_operations.rb
ruby -Ilib examples/07_segments_and_shapes.rb
ruby -Ilib examples/08_geodetic_arithmetic.rb
ruby -Ilib examples/09_geojson_export.rb
ruby -Ilib examples/10_wkt_serialization.rb
ruby -Ilib examples/11_wkb_serialization.rb
ruby -Ilib examples/12_geos_benchmark.rb
ruby -Ilib examples/13_geos_operations.rb
ruby -Ilib examples/14_geos_map_rendering.rb
This library’s coordinate conversion and distance algorithms have been tested in production GIS applications for over 20 years. The codebase is thorough:
The math is vetted. The conversions are reversible. The distances are accurate to within meters across the globe.
Full documentation is at madbomber.github.io/geodetic
This release adds the Map adapter pattern for rendering Geodetic objects on raster maps:
Geodetic::Map::Base — abstract interface for pluggable map backendsGeodetic::Map::LibGdGis — concrete adapter for PNG output via libgd-gisadd_to_map(map, **style) method on all geometriesPrevious releases added:
MIT. Use freely.
Geodetic is maintained by Dewayne VanHoozer. It draws on 20 years of GIS application development across transportation, surveying, and geospatial analysis projects.