Class: OpenLocationCode::Encoder

Inherits:
Object
  • Object
show all
Defined in:
lib/open_location_code/encoder.rb

Overview

Encode latitude longitude to code

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(latitude, longitude, code_length) ⇒ Encoder

Returns a new instance of Encoder.



8
9
10
11
12
13
14
15
16
# 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

Instance Attribute Details

#code_lengthObject

Returns the value of attribute code_length.



6
7
8
# File 'lib/open_location_code/encoder.rb', line 6

def code_length
  @code_length
end

#latitudeObject

Returns the value of attribute latitude.



6
7
8
# File 'lib/open_location_code/encoder.rb', line 6

def latitude
  @latitude
end

#longitudeObject

Returns the value of attribute longitude.



6
7
8
# File 'lib/open_location_code/encoder.rb', line 6

def longitude
  @longitude
end

#originalObject

Returns the value of attribute original.



6
7
8
# File 'lib/open_location_code/encoder.rb', line 6

def original
  @original
end

Instance Method Details

#clip_latitude(latitude) ⇒ Float

Clip a latitude into the range -90 to 90.

Parameters:

  • latitude (Float)

Returns:

  • (Float)


24
25
26
# File 'lib/open_location_code/encoder.rb', line 24

def clip_latitude(latitude)
  [ 90, [-90, latitude].max ].min
end

#compute_latitude_precisionObject

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.


52
53
54
55
56
57
58
# 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) ⇒ Object

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


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# 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) ⇒ Object

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.

Parameters:

  • code_length (Integer)

    The number of significant digits in the output code, not including any separator characters.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# 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) ⇒ Float

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

Parameters:

  • longitude (Float)

Returns:

  • (Float)


34
35
36
37
38
39
40
41
42
43
44
# 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

#processString

Encode latitude and longitude

Returns:

  • (String)


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# 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