class OpenLocationCode::Encoder

Encode latitude longitude to code

Attributes

code_length[RW]
latitude[RW]
longitude[RW]
original[RW]

Public Class Methods

new(latitude, longitude, code_length) click to toggle source
# File lib/open_location_code/encoder.rb, line 8
def initialize(latitude, longitude, code_length)
  @code_length = code_length

  # Ensure that latitude and longitude are valid.
  @latitude = clip_latitude(latitude)
  @longitude = normalize_longitude(longitude)

  @original = { latitude: latitude, longitude: longitude }
end

Public Instance Methods

clip_latitude(latitude) click to toggle source

Clip a latitude into the range -90 to 90.

@param [Float] latitude @return [Float]

# File lib/open_location_code/encoder.rb, line 24
def clip_latitude(latitude)
  [ 90, [-90, latitude].max ].min
end
compute_latitude_precision() click to toggle source

Compute the latitude precision value for a given code length. Lengths <= 10 have the same precision for latitude and longitude, but lengths > 10 have different precisions due to the grid method having fewer columns than rows.

# File lib/open_location_code/encoder.rb, line 52
def compute_latitude_precision
  if code_length <= 10
    return 20**(code_length/-2.0 + 2).floor
  end

  (20**-3).to_f/GRID_ROWS**(code_length - 10)
end
encode_grid(code_length) click to toggle source

Encode a location using the grid refinement method into an OLC string.

The grid refinement method divides the area into a grid of 4x5, and uses a single character to refine the area. This allows default accuracy OLC codes to be refined with just a single character.

@param [Integer] code_length

# File lib/open_location_code/encoder.rb, line 125
def encode_grid(code_length)
  code = ''
  lat_place_value = GRID_SIZE_DEGREES
  lng_place_value = GRID_SIZE_DEGREES

  # Adjust latitude and longitude so they fall into positive ranges and
  # get the offset for the required places.
  adjusted_latitude = (latitude + LATITUDE_MAX) % lat_place_value
  adjusted_longitude = (longitude + LONGITUDE_MAX) % lng_place_value

  code_length.times do |i|
    # Work out the row and column.
    row = (adjusted_latitude / (lat_place_value.to_f / GRID_ROWS)).floor
    col = (adjusted_longitude / (lng_place_value.to_f / GRID_COLUMNS)).floor

    lat_place_value /= GRID_ROWS
    lng_place_value /= GRID_COLUMNS

    adjusted_latitude -= (row * lat_place_value)
    adjusted_longitude -= (col * lng_place_value)

    code += CODE_ALPHABET[row * GRID_COLUMNS + col]
  end

  return code
end
encode_pairs(code_length) click to toggle source

Encode a location into a sequence of OLC lat/lng pairs.

This uses pairs of characters (longitude and latitude in that order) to represent each step in a 20x20 grid. Each code, therefore, has 1/400th the area of the previous code.

@param [Integer] code_length

The number of significant digits in the output code, not
including any separator characters.
# File lib/open_location_code/encoder.rb, line 71
def encode_pairs(code_length)
  code = ''

  # Adjust latitude and longitude so they fall into positive ranges.
  adjusted_latitude = latitude + LATITUDE_MAX
  adjusted_longitude = longitude + LONGITUDE_MAX

  # Count digits - can't use string length because it may include a separator
  # character.
  digit_count = 0

  while (digit_count < code_length) do
    # Provides the value of digits in this place in decimal degrees.
    place_value = PAIR_RESOLUTIONS[(digit_count / 2.0).floor]

    # Do the latitude - gets the digit for this place and subtracts that for
    # the next digit.
    digit_value = (adjusted_latitude / place_value.to_f).floor
    adjusted_latitude -= (digit_value * place_value)
    code += CODE_ALPHABET[digit_value]
    digit_count += 1

    # And do the longitude - gets the digit for this place and subtracts that
    # for the next digit.
    digit_value = (adjusted_longitude / place_value.to_f).floor
    adjusted_longitude -= (digit_value * place_value)
    code += CODE_ALPHABET[digit_value]
    digit_count += 1

    # Should we add a separator here?
    if digit_count == SEPARATOR_POSITION && digit_count < code_length
      code += SEPARATOR
    end
  end

  if code.length < SEPARATOR_POSITION
    code += PADDING_CHARACTER*(SEPARATOR_POSITION - code.length + 1)
  end

  if code.length == SEPARATOR_POSITION
    code += SEPARATOR
  end

  return code
end
normalize_longitude(longitude) click to toggle source

Normalize a longitude into the range -180 to 180, not including 180.

@param [Float] longitude @return [Float]

# File lib/open_location_code/encoder.rb, line 34
def normalize_longitude(longitude)
  while (longitude < -180) do
    longitude += 360
  end

  while (longitude >= 180) do
    longitude -= 360
  end

  longitude
end
process() click to toggle source

Encode latitude and longitude

@return [String]

# File lib/open_location_code/encoder.rb, line 157
def process
  if code_length < 2 || (code_length < SEPARATOR_POSITION && code_length % 2 == 1)
    raise OLCError, 'Invalid Open Location Code length'
  end

  # Latitude 90 needs to be adjusted to be just less, so the returned code
  # can also be decoded.
  if latitude == 90
    self.latitude -= compute_latitude_precision
  end

  code = encode_pairs([code_length, PAIR_CODE_LENGTH].min)

  # If the requested length indicates we want grid refined codes.
  if code_length > PAIR_CODE_LENGTH
    code += encode_grid(code_length - PAIR_CODE_LENGTH)
  end

  code
end