Geodetic: One Gem to Convert Them All

Geodetic

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:

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:

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

What’s New in 0.8.0

This release adds the Map adapter pattern for rendering Geodetic objects on raster maps:

Previous releases added:

Getting Help

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.