Geodetic: One Gem to Convert Them All

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.
What is Geodetic?
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"
18 Coordinate Systems
Convert between any of these coordinate systems with full precision:
- Standard systems: LLA (lat/lng/altitude), ECEF (3D Cartesian), UTM (zones 1-60), Web Mercator, UPS (polar)
- Local reference frames: ENU (East/North/Up), NED (North/East/Down)
- Military/aviation: MGRS, USNG, State Plane, UPS
- UK specific: BNG (British National Grid)
- Spatial hashes (all bidirectional): GH36 (radix-36), GH (base-32), HAM (Maidenhead radio grid), OLC (Google Plus Codes), GEOREF, GARS
- Hierarchical spatial index: H3 (Uber’s hexagonal system)
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 and Bearing with Unit Tracking
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.
Geometry Operations
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)
Geoid Height and Vertical Datums
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")
Map Rendering (Pluggable Adapters)
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.
Optional GEOS Acceleration
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.
GeoJSON, WKT, and WKB Support
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)
Immutable Value Types
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)
14 Runnable Examples
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
Production Foundation
This library’s coordinate conversion and distance algorithms have been tested in production GIS applications for over 20 years. The codebase is thorough:
- 1000+ tests covering coordinate conversions, edge cases, and boundary conditions
- 16 different coordinate systems with spatial hashing (neighbors, grid cells, precision)
- Validated setters with type coercion and range constraints on all numeric coordinates
- Datum support for 15 different reference ellipsoids (WGS84, Clarke 1866, GRS 1980, etc.)
- Comprehensive documentation with full API reference and worked examples
The math is vetted. The conversions are reversible. The distances are accurate to within meters across the globe.
Documentation
Full documentation is at madbomber.github.io/geodetic
- Getting Started guide with quick examples
- Complete API reference for each coordinate system
- Feature guides: distance calculations, bearing/compass, areas, paths, serialization
- 14 working examples covering all major features
What’s New in 0.8.0
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 geometries- Example 14 — visualizes GEOS operations on a single raster map with legend
Previous releases added:
- 0.7.0: GEOS acceleration for polygon operations and point-in-polygon testing
- 0.6.0: WKT and WKB serialization (PostGIS standard formats)
- 0.5.x: GeoJSON export/import, Vector class, Geodetic arithmetic with + and * operators, corridors
- 0.4.0: Segment class, Path operations, Triangle/Rectangle/Pentagon/Hexagon/Octagon polygon subclasses
- 0.3.x: Path class, Feature wrapper, H3 hexagonal indexing, GEOREF, GARS systems
- 0.2.0: GH36, GH, HAM, OLC spatial hashes
- 0.1.0: 15 coordinate systems, distance/bearing calculations, areas
Getting Help
- Documentation: madbomber.github.io/geodetic
- GitHub: github.com/madbomber/geodetic
- Issues: Report bugs and request features
License
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.