Geodetic::Coordinate::OLC¶
Open Location Code (Plus Codes)¶
Open Location Code (OLC), also known as Plus Codes, is Google's open system for encoding locations into short, URL-friendly codes like 849VCWC8+R9. It uses a 20-character alphabet (23456789CFGHJMPQRVWX) that excludes vowels and visually ambiguous characters to avoid spelling words or confusing similar-looking characters.
The encoding uses a false coordinate origin that shifts longitude by +180 and latitude by +90, making all values positive before encoding. Two encoding modes produce codes of varying precision:
| Mode | Characters | Encoding | Resolution per Level |
|---|---|---|---|
| Paired | 1-10 (5 pairs) | Base-20 lat/lng pairs | 20, 1, 0.05, 0.0025, 0.000125 degrees |
| Grid | 11-15 | 5x4 grid refinement | Each character subdivides the remaining cell |
All codes include a + separator after the 8th character position. Codes shorter than 8 significant characters are padded with 0. For example, precision 2 produces 84000000+.
OLC is a 2D coordinate system (no altitude). Conversions to/from other systems go through LLA as the intermediary. Each code represents a rectangular cell; the coordinate's point value is the cell's midpoint.
Constructor¶
# From a plus code string
coord = Geodetic::Coordinate::OLC.new("849VCWC8+R9")
# From any coordinate (converts via LLA)
coord = Geodetic::Coordinate::OLC.new(lla_coord)
coord = Geodetic::Coordinate::OLC.new(utm_coord, precision: 11)
| Parameter | Type | Default | Description |
|---|---|---|---|
source |
String or Coord | — | A plus code string or any coordinate object |
precision |
Integer | 10 | Code length: 2, 4, 6, 8, 10, 11, 12, 13, 14, or 15 |
Raises ArgumentError if the source string is empty, lacks a + separator at position 8, contains invalid characters, has odd padding positions, or has characters after + when padded. String input is case-insensitive (normalized to uppercase).
Attributes¶
| Attribute | Type | Access | Description |
|---|---|---|---|
code |
String | read-only | The plus code string |
OLC is immutable — there are no setter methods.
Precision¶
The precision (code length) determines the size of the encoded cell:
| Length | Mode | Approximate Cell Size |
|---|---|---|
| 2 | Paired | ~2,220 km x 2,220 km |
| 4 | Paired | ~111 km x 111 km |
| 6 | Paired | ~5.5 km x 5.5 km |
| 8 | Paired | ~275 m x 275 m |
| 10 | Paired | ~14 m x 14 m |
| 11 | Grid | ~3 m x 3.5 m |
| 12 | Grid | ~0.6 m x 0.9 m |
| 13 | Grid | ~0.1 m x 0.2 m |
| 14 | Grid | ~0.02 m x 0.05 m |
| 15 | Grid | ~0.005 m x 0.01 m |
coord.precision # => 10 (number of significant characters)
coord.precision_in_meters # => { lat: ~13.9, lng: ~13.9 }
Conversions¶
All conversions chain through LLA. The datum parameter defaults to Geodetic::WGS84.
Instance Methods¶
coord.to_lla # => LLA (midpoint of the cell)
coord.to_ecef
coord.to_utm
coord.to_enu(reference_lla)
coord.to_ned(reference_lla)
coord.to_mgrs
coord.to_usng
coord.to_web_mercator
coord.to_ups
coord.to_state_plane(zone_code)
coord.to_bng
coord.to_gh36
coord.to_gh
coord.to_ham
Class Methods¶
OLC.from_lla(lla_coord)
OLC.from_ecef(ecef_coord)
OLC.from_utm(utm_coord)
OLC.from_web_mercator(wm_coord)
OLC.from_gh(gh_coord)
OLC.from_gh36(gh36_coord)
OLC.from_ham(ham_coord)
# ... and all other coordinate systems
LLA Convenience Methods¶
lla = Geodetic::Coordinate::LLA.new(lat: 37.4220, lng: -122.0841)
olc = lla.to_olc # default precision 10
olc = lla.to_olc(precision: 11) # grid refinement precision
lla = Geodetic::Coordinate::LLA.from_olc(olc)
Serialization¶
to_s(truncate_to = nil)¶
Returns the plus code string. An optional integer truncates to that precision (re-encodes from decoded coordinates).
coord = OLC.new("849VCWC8+R9")
coord.to_s # => "849VCWC8+R9"
coord.to_s(10) # => "849VCWC8+R9" (already 11 chars, truncated to 10)
coord.to_s(8) # => "849VCWC8+"
coord.to_s(4) # => "84900000+"
to_a¶
Returns [lat, lng] of the cell midpoint.
from_string / from_array¶
OLC.from_string("849VCWC8+R9") # from plus code string
OLC.from_array([37.4220, -122.0841]) # from [lat, lng]
Neighbors¶
Returns all 8 adjacent cells as OLC instances.
coord = OLC.new("849VCWC8+R9")
neighbors = coord.neighbors
# => { N: OLC, S: OLC, E: OLC, W: OLC, NE: OLC, NW: OLC, SE: OLC, SW: OLC }
neighbors[:N].to_lla.lat > coord.to_lla.lat # => true
neighbors[:E].to_lla.lng > coord.to_lla.lng # => true
Neighbors preserve the same precision as the original code. Latitude is clamped to valid range near the poles; longitude wraps at the antimeridian.
Area¶
The to_area method returns the plus code cell as a Geodetic::Areas::BoundingBox.
area = coord.to_area
# => Geodetic::Areas::BoundingBox
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
area.nw # => LLA (northwest corner)
area.se # => LLA (southeast corner)
Equality¶
Two OLC instances are equal if their code strings match exactly.
OLC.new("849VCWC8+R9") == OLC.new("849VCWC8+R9") # => true
OLC.new("849VCWC8+R9") == OLC.new("849VCWC8+R8") # => false
valid?¶
Returns true if the code has a + separator at position 8, valid characters, and correct padding.
Universal Distance and Bearing Methods¶
OLC supports all universal distance and bearing methods via the DistanceMethods and BearingMethods mixins:
a = OLC.new("849VCWC8+R9")
b = OLC.new("87G8Q2JM+QV")
a.distance_to(b) # => Distance
a.straight_line_distance_to(b) # => Distance
a.bearing_to(b) # => Bearing
a.elevation_to(b) # => Float (degrees)
Well-Known Plus Codes¶
| Location | Plus Code |
|---|---|
| Google HQ (Mountain View) | 849VCWC8+R9 |
| Statue of Liberty | 87G8Q2JM+QV |
| Eiffel Tower | 8FW4V75V+8Q |
| Sydney Opera House | 4RRH46R6+PJ |
| Null Island (0, 0) | 6FG22222+22 |
Alphabet¶
The 20-character OLC alphabet: 2 3 4 5 6 7 8 9 C F G H J M P Q R V W X
Characters were chosen to avoid: - Vowels (no words can be spelled) - Visually ambiguous characters (no 0/O, 1/I/L) - Characters easily confused in handwriting