open-location-code 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/lib/plus_codes.rb +26 -0
 - data/lib/plus_codes/code_area.rb +38 -0
 - data/lib/plus_codes/open_location_code.rb +264 -0
 - data/test/plus_codes_test.rb +90 -0
 - metadata +119 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA1:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 378b33bb5f5bdf0ec82c286db7e7b54b762f2723
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: b2f0ded83b4ed9c914a0220c177ae6bb3e43adf5
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 052d9fb0aeeab2fd7969ae25b00824813e9f63b4877d7e73d5a5aa75d5d51c17fc1eeb51482c1f08f8cfa3d50ca3793ca2e44263c225c9244b9e5fa541f94c51
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 34f7c50ad9dd924e7afaa441ab3cdefe80b32de655b97cdb17f5c06d3c698738855e5c80af8d3c08f1cdce82bb494e23b22580933893d8a77cfb686fe4fa5f47
         
     | 
    
        data/lib/plus_codes.rb
    ADDED
    
    | 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Plus+Codes is a Ruby implementation of Google Open Location Code(Plus+Codes).
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            # @author We-Ming Wu
         
     | 
| 
      
 4 
     | 
    
         
            +
            module PlusCodes
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              # A separator used to separate the code into two parts.
         
     | 
| 
      
 7 
     | 
    
         
            +
              SEPARATOR = '+'.freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              # The max number of characters can be placed before the separator.
         
     | 
| 
      
 10 
     | 
    
         
            +
              SEPARATOR_POSITION = 8
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              # The character used to pad a code
         
     | 
| 
      
 13 
     | 
    
         
            +
              PADDING = '0'.freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              # The character set used to encode coordinates.
         
     | 
| 
      
 16 
     | 
    
         
            +
              CODE_ALPHABET = '23456789CFGHJMPQRVWX'.freeze
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # ASCII lookup table.
         
     | 
| 
      
 19 
     | 
    
         
            +
              DECODE = (CODE_ALPHABET.chars + [PADDING, SEPARATOR]).reduce([]) do |ary, c|
         
     | 
| 
      
 20 
     | 
    
         
            +
                ary[c.ord] = CODE_ALPHABET.index(c)
         
     | 
| 
      
 21 
     | 
    
         
            +
                ary[c.downcase.ord] = CODE_ALPHABET.index(c)
         
     | 
| 
      
 22 
     | 
    
         
            +
                ary[c.ord] ||= -1
         
     | 
| 
      
 23 
     | 
    
         
            +
                ary
         
     | 
| 
      
 24 
     | 
    
         
            +
              end.freeze
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module PlusCodes
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              # [CodeArea] contains coordinates of a decoded Open Location Code(Plus+Codes).
         
     | 
| 
      
 4 
     | 
    
         
            +
              # The coordinates include the latitude and longitude of the lower left and
         
     | 
| 
      
 5 
     | 
    
         
            +
              # upper right corners and the center of the bounding box for the area the
         
     | 
| 
      
 6 
     | 
    
         
            +
              # code represents.
         
     | 
| 
      
 7 
     | 
    
         
            +
              #
         
     | 
| 
      
 8 
     | 
    
         
            +
              # @author We-Ming Wu
         
     | 
| 
      
 9 
     | 
    
         
            +
              class CodeArea
         
     | 
| 
      
 10 
     | 
    
         
            +
                attr_accessor :south_latitude, :west_longitude, :latitude_height,
         
     | 
| 
      
 11 
     | 
    
         
            +
                :longitude_width, :latitude_center, :longitude_center
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # Creates a [CodeArea].
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                # @param south_latitude [Numeric] the latitude of the SW corner in degrees
         
     | 
| 
      
 16 
     | 
    
         
            +
                # @param west_longitude [Numeric] the longitude of the SW corner in degrees
         
     | 
| 
      
 17 
     | 
    
         
            +
                # @param latitude_height [Numeric] the height from the SW corner in degrees
         
     | 
| 
      
 18 
     | 
    
         
            +
                # @param longitude_width [Numeric] the width from the SW corner in degrees
         
     | 
| 
      
 19 
     | 
    
         
            +
                # @return [CodeArea] a code area which contains the coordinates
         
     | 
| 
      
 20 
     | 
    
         
            +
                def initialize(south_latitude, west_longitude, latitude_height, longitude_width)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @south_latitude = south_latitude
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @west_longitude = west_longitude
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @latitude_height = latitude_height
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @longitude_width = longitude_width
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @latitude_center = south_latitude + latitude_height / 2.0
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @longitude_center = west_longitude + longitude_width / 2.0
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def north_latitude
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @south_latitude + @latitude_height
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def east_longitude
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @west_longitude + @longitude_width
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,264 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative '../plus_codes'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative '../plus_codes/code_area'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module PlusCodes
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              # [OpenLocationCode] implements the Google Open Location Code(Plus+Codes) algorithm.
         
     | 
| 
      
 7 
     | 
    
         
            +
              #
         
     | 
| 
      
 8 
     | 
    
         
            +
              # @author We-Ming Wu
         
     | 
| 
      
 9 
     | 
    
         
            +
              class OpenLocationCode
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # Determines if a string is a valid sequence of Open Location Code(Plus+Codes) characters.
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @param code [String] a plus+codes
         
     | 
| 
      
 14 
     | 
    
         
            +
                # @return [TrueClass, FalseClass] true if the code is valid, false otherwise
         
     | 
| 
      
 15 
     | 
    
         
            +
                def valid?(code)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  valid_length?(code) &&
         
     | 
| 
      
 17 
     | 
    
         
            +
                  valid_separator?(code) &&
         
     | 
| 
      
 18 
     | 
    
         
            +
                  valid_padding?(code) &&
         
     | 
| 
      
 19 
     | 
    
         
            +
                  valid_character?(code)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                # Determines if a string is a valid short Open Location Code(Plus+Codes).
         
     | 
| 
      
 23 
     | 
    
         
            +
                #
         
     | 
| 
      
 24 
     | 
    
         
            +
                # @param code [String] a plus+codes
         
     | 
| 
      
 25 
     | 
    
         
            +
                # @return [TrueClass, FalseClass] true if the code is short, false otherwise
         
     | 
| 
      
 26 
     | 
    
         
            +
                def short?(code)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  valid?(code) && code.index(SEPARATOR) < SEPARATOR_POSITION
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                # Determines if a string is a valid full Open Location Code(Plus+Codes).
         
     | 
| 
      
 31 
     | 
    
         
            +
                #
         
     | 
| 
      
 32 
     | 
    
         
            +
                # @param code [String] a plus+codes
         
     | 
| 
      
 33 
     | 
    
         
            +
                # @return [TrueClass, FalseClass] true if the code is full, false otherwise
         
     | 
| 
      
 34 
     | 
    
         
            +
                def full?(code)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  valid?(code) && !short?(code)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                # Converts a latitude and longitude into a Open Location Code(Plus+Codes).
         
     | 
| 
      
 39 
     | 
    
         
            +
                #
         
     | 
| 
      
 40 
     | 
    
         
            +
                # @param latitude [Numeric] a latitude in degrees
         
     | 
| 
      
 41 
     | 
    
         
            +
                # @param longitude [Numeric] a longitude in degrees
         
     | 
| 
      
 42 
     | 
    
         
            +
                # @param code_length [Integer] the number of characters in the code, this excludes the separator
         
     | 
| 
      
 43 
     | 
    
         
            +
                # @return [String] a plus+codes
         
     | 
| 
      
 44 
     | 
    
         
            +
                def encode(latitude, longitude, code_length = 10)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  raise ArgumentError,
         
     | 
| 
      
 46 
     | 
    
         
            +
                  "Invalid Open Location Code(Plus+Codes) length: #{code_length}" if invalid_length?(code_length)
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  latitude = clip_latitude(latitude)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  longitude = normalize_longitude(longitude)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  latitude -= precision_by_length(code_length) if latitude == 90
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  lat = (latitude + 90).to_r
         
     | 
| 
      
 53 
     | 
    
         
            +
                  lng = (longitude + 180).to_r
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  digit = 0
         
     | 
| 
      
 56 
     | 
    
         
            +
                  code = ''
         
     | 
| 
      
 57 
     | 
    
         
            +
                  while digit < code_length
         
     | 
| 
      
 58 
     | 
    
         
            +
                    lat, lng = narrow_region(digit, lat, lng)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    digit, lat, lng = build_code(digit, code, lat, lng)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    code << SEPARATOR if (digit == SEPARATOR_POSITION)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  digit < SEPARATOR_POSITION ? padded(code) : code
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                # Decodes an Open Location Code(Plus+Codes) into a [CodeArea].
         
     | 
| 
      
 67 
     | 
    
         
            +
                #
         
     | 
| 
      
 68 
     | 
    
         
            +
                # @param code [String] a plus+codes
         
     | 
| 
      
 69 
     | 
    
         
            +
                # @return [CodeArea] a code area which contains the coordinates
         
     | 
| 
      
 70 
     | 
    
         
            +
                def decode(code)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  raise ArgumentError,
         
     | 
| 
      
 72 
     | 
    
         
            +
                  "Open Location Code(Plus+Codes) is not a valid full code: #{code}" unless full?(code)
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  code = code.gsub(SEPARATOR, '')
         
     | 
| 
      
 75 
     | 
    
         
            +
                  code = code.gsub(/#{PADDING}+/, '')
         
     | 
| 
      
 76 
     | 
    
         
            +
                  code = code.upcase
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  south_latitude = -90.0
         
     | 
| 
      
 79 
     | 
    
         
            +
                  west_longitude = -180.0
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  lat_resolution = 400.to_r
         
     | 
| 
      
 82 
     | 
    
         
            +
                  lng_resolution = 400.to_r
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  digit = 0
         
     | 
| 
      
 85 
     | 
    
         
            +
                  while digit < code.length
         
     | 
| 
      
 86 
     | 
    
         
            +
                    if digit < 10
         
     | 
| 
      
 87 
     | 
    
         
            +
                      lat_resolution /= 20
         
     | 
| 
      
 88 
     | 
    
         
            +
                      lng_resolution /= 20
         
     | 
| 
      
 89 
     | 
    
         
            +
                      south_latitude += lat_resolution * DECODE[code[digit].ord]
         
     | 
| 
      
 90 
     | 
    
         
            +
                      west_longitude += lng_resolution * DECODE[code[digit + 1].ord]
         
     | 
| 
      
 91 
     | 
    
         
            +
                      digit += 2
         
     | 
| 
      
 92 
     | 
    
         
            +
                    else
         
     | 
| 
      
 93 
     | 
    
         
            +
                      lat_resolution /= 5
         
     | 
| 
      
 94 
     | 
    
         
            +
                      lng_resolution /= 4
         
     | 
| 
      
 95 
     | 
    
         
            +
                      row = DECODE[code[digit].ord] / 4
         
     | 
| 
      
 96 
     | 
    
         
            +
                      column = DECODE[code[digit].ord] % 4
         
     | 
| 
      
 97 
     | 
    
         
            +
                      south_latitude += lat_resolution * row
         
     | 
| 
      
 98 
     | 
    
         
            +
                      west_longitude += lng_resolution * column
         
     | 
| 
      
 99 
     | 
    
         
            +
                      digit += 1
         
     | 
| 
      
 100 
     | 
    
         
            +
                    end
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                  CodeArea.new(south_latitude, west_longitude, lat_resolution, lng_resolution)
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                # Recovers a full Open Location Code(Plus+Codes) from a short code and a reference location.
         
     | 
| 
      
 107 
     | 
    
         
            +
                #
         
     | 
| 
      
 108 
     | 
    
         
            +
                # @param short_code [String] a plus+codes
         
     | 
| 
      
 109 
     | 
    
         
            +
                # @param reference_latitude [Numeric] a reference latitude in degrees
         
     | 
| 
      
 110 
     | 
    
         
            +
                # @param reference_longitude [Numeric] a reference longitude in degrees
         
     | 
| 
      
 111 
     | 
    
         
            +
                # @return [String] a plus+codes
         
     | 
| 
      
 112 
     | 
    
         
            +
                def recover_nearest(short_code, reference_latitude, reference_longitude)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  return short_code if full?(short_code)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  raise ArgumentError,
         
     | 
| 
      
 115 
     | 
    
         
            +
                  "Open Location Code(Plus+Codes) is not valid: #{short_code}" unless short?(short_code)
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                  ref_lat = clip_latitude(reference_latitude)
         
     | 
| 
      
 118 
     | 
    
         
            +
                  ref_lng = normalize_longitude(reference_longitude)
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  prefix_len = SEPARATOR_POSITION - short_code.index(SEPARATOR)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  code = prefix_by_reference(ref_lat, ref_lng, prefix_len) << short_code
         
     | 
| 
      
 122 
     | 
    
         
            +
                  code_area = decode(code)
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                  area_range = precision_by_length(prefix_len)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  area_edge = area_range / 2
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  latitude = code_area.latitude_center
         
     | 
| 
      
 128 
     | 
    
         
            +
                  latitude_diff = latitude - ref_lat
         
     | 
| 
      
 129 
     | 
    
         
            +
                  if (latitude_diff > area_edge)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    latitude -= area_range
         
     | 
| 
      
 131 
     | 
    
         
            +
                  elsif (latitude_diff < -area_edge)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    latitude += area_range
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  longitude = code_area.longitude_center
         
     | 
| 
      
 136 
     | 
    
         
            +
                  longitude_diff = longitude - ref_lng
         
     | 
| 
      
 137 
     | 
    
         
            +
                  if (longitude_diff > area_edge)
         
     | 
| 
      
 138 
     | 
    
         
            +
                    longitude -= area_range
         
     | 
| 
      
 139 
     | 
    
         
            +
                  elsif (longitude_diff < -area_edge)
         
     | 
| 
      
 140 
     | 
    
         
            +
                    longitude += area_range
         
     | 
| 
      
 141 
     | 
    
         
            +
                  end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                  encode(latitude, longitude, code.length - SEPARATOR.length)
         
     | 
| 
      
 144 
     | 
    
         
            +
                end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                # Removes four, six or eight digits from the front of an Open Location Code(Plus+Codes) given a reference location.
         
     | 
| 
      
 147 
     | 
    
         
            +
                #
         
     | 
| 
      
 148 
     | 
    
         
            +
                # @param code [String] a plus+codes
         
     | 
| 
      
 149 
     | 
    
         
            +
                # @param latitude [Numeric] a latitude in degrees
         
     | 
| 
      
 150 
     | 
    
         
            +
                # @param longitude [Numeric] a longitude in degrees
         
     | 
| 
      
 151 
     | 
    
         
            +
                # @return [String] a short plus+codes
         
     | 
| 
      
 152 
     | 
    
         
            +
                def shorten(code, latitude, longitude)
         
     | 
| 
      
 153 
     | 
    
         
            +
                  raise ArgumentError,
         
     | 
| 
      
 154 
     | 
    
         
            +
                  "Open Location Code(Plus+Codes) is a valid full code: #{code}" unless full?(code)
         
     | 
| 
      
 155 
     | 
    
         
            +
                  raise ArgumentError,
         
     | 
| 
      
 156 
     | 
    
         
            +
                  "Cannot shorten padded codes: #{code}" unless code.index(PADDING).nil?
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                  code_area = decode(code)
         
     | 
| 
      
 159 
     | 
    
         
            +
                  lat_diff = (latitude - code_area.latitude_center).abs
         
     | 
| 
      
 160 
     | 
    
         
            +
                  lng_diff = (longitude - code_area.longitude_center).abs
         
     | 
| 
      
 161 
     | 
    
         
            +
                  max_diff = [lat_diff, lng_diff].max
         
     | 
| 
      
 162 
     | 
    
         
            +
                  [8, 6, 4].each do |removal_len|
         
     | 
| 
      
 163 
     | 
    
         
            +
                    area_edge = precision_by_length(removal_len + 2) / 2
         
     | 
| 
      
 164 
     | 
    
         
            +
                    return code[removal_len..-1] if max_diff < area_edge
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                  code.upcase
         
     | 
| 
      
 168 
     | 
    
         
            +
                end
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                private
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                def prefix_by_reference(latitude, longitude, prefix_len)
         
     | 
| 
      
 173 
     | 
    
         
            +
                  precision = precision_by_length(prefix_len)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  rounded_latitude = (latitude / precision).floor * precision
         
     | 
| 
      
 175 
     | 
    
         
            +
                  rounded_longitude = (longitude / precision).floor * precision
         
     | 
| 
      
 176 
     | 
    
         
            +
                  encode(rounded_latitude, rounded_longitude)[0...prefix_len]
         
     | 
| 
      
 177 
     | 
    
         
            +
                end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                def narrow_region(digit, latitude, longitude)
         
     | 
| 
      
 180 
     | 
    
         
            +
                  if digit == 0
         
     | 
| 
      
 181 
     | 
    
         
            +
                    latitude /= 20
         
     | 
| 
      
 182 
     | 
    
         
            +
                    longitude /= 20
         
     | 
| 
      
 183 
     | 
    
         
            +
                  elsif digit < 10
         
     | 
| 
      
 184 
     | 
    
         
            +
                    latitude *= 20
         
     | 
| 
      
 185 
     | 
    
         
            +
                    longitude *= 20
         
     | 
| 
      
 186 
     | 
    
         
            +
                  else
         
     | 
| 
      
 187 
     | 
    
         
            +
                    latitude *= 5
         
     | 
| 
      
 188 
     | 
    
         
            +
                    longitude *= 4
         
     | 
| 
      
 189 
     | 
    
         
            +
                  end
         
     | 
| 
      
 190 
     | 
    
         
            +
                  [latitude, longitude]
         
     | 
| 
      
 191 
     | 
    
         
            +
                end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                def build_code(digit_count, code, latitude, longitude)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  lat_digit = latitude.to_i
         
     | 
| 
      
 195 
     | 
    
         
            +
                  lng_digit = longitude.to_i
         
     | 
| 
      
 196 
     | 
    
         
            +
                  if digit_count < 10
         
     | 
| 
      
 197 
     | 
    
         
            +
                    code << CODE_ALPHABET[lat_digit]
         
     | 
| 
      
 198 
     | 
    
         
            +
                    code << CODE_ALPHABET[lng_digit]
         
     | 
| 
      
 199 
     | 
    
         
            +
                    [digit_count + 2, latitude - lat_digit, longitude - lng_digit]
         
     | 
| 
      
 200 
     | 
    
         
            +
                  else
         
     | 
| 
      
 201 
     | 
    
         
            +
                    code << CODE_ALPHABET[4 * lat_digit + lng_digit]
         
     | 
| 
      
 202 
     | 
    
         
            +
                    [digit_count + 1, latitude - lat_digit, longitude - lng_digit]
         
     | 
| 
      
 203 
     | 
    
         
            +
                  end
         
     | 
| 
      
 204 
     | 
    
         
            +
                end
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                def valid_length?(code)
         
     | 
| 
      
 207 
     | 
    
         
            +
                  !code.nil? && code.length >= 2 + SEPARATOR.length && code.split(SEPARATOR).last.length != 1
         
     | 
| 
      
 208 
     | 
    
         
            +
                end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                def valid_separator?(code)
         
     | 
| 
      
 211 
     | 
    
         
            +
                  separator_idx = code.index(SEPARATOR)
         
     | 
| 
      
 212 
     | 
    
         
            +
                  code.count(SEPARATOR) == 1 && separator_idx <= SEPARATOR_POSITION && separator_idx.even?
         
     | 
| 
      
 213 
     | 
    
         
            +
                end
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                def valid_padding?(code)
         
     | 
| 
      
 216 
     | 
    
         
            +
                  if code.include?(PADDING)
         
     | 
| 
      
 217 
     | 
    
         
            +
                    return false if code.start_with?(PADDING)
         
     | 
| 
      
 218 
     | 
    
         
            +
                    return false if code[-2..-1] != PADDING + SEPARATOR
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                    paddings = code.scan(/#{PADDING}+/)
         
     | 
| 
      
 221 
     | 
    
         
            +
                    return false if !paddings.one? || paddings[0].length.odd?
         
     | 
| 
      
 222 
     | 
    
         
            +
                    return false if paddings[0].length > SEPARATOR_POSITION - 2
         
     | 
| 
      
 223 
     | 
    
         
            +
                  end
         
     | 
| 
      
 224 
     | 
    
         
            +
                  true
         
     | 
| 
      
 225 
     | 
    
         
            +
                end
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                def valid_character?(code)
         
     | 
| 
      
 228 
     | 
    
         
            +
                  code.chars.each { |ch| return false if DECODE[ch.ord].nil? }
         
     | 
| 
      
 229 
     | 
    
         
            +
                  true
         
     | 
| 
      
 230 
     | 
    
         
            +
                end
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
      
 232 
     | 
    
         
            +
                def invalid_length?(code_length)
         
     | 
| 
      
 233 
     | 
    
         
            +
                  code_length < 2 || (code_length < SEPARATOR_POSITION && code_length.odd?)
         
     | 
| 
      
 234 
     | 
    
         
            +
                end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                def padded(code)
         
     | 
| 
      
 237 
     | 
    
         
            +
                  code << PADDING * (SEPARATOR_POSITION - code.length) << SEPARATOR
         
     | 
| 
      
 238 
     | 
    
         
            +
                end
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                def precision_by_length(code_length)
         
     | 
| 
      
 241 
     | 
    
         
            +
                  if code_length <= 10
         
     | 
| 
      
 242 
     | 
    
         
            +
                    precision = 20 ** ((code_length / -2).to_i + 2)
         
     | 
| 
      
 243 
     | 
    
         
            +
                  else
         
     | 
| 
      
 244 
     | 
    
         
            +
                    precision = (20 ** -3) / (5 ** (code_length - 10))
         
     | 
| 
      
 245 
     | 
    
         
            +
                  end
         
     | 
| 
      
 246 
     | 
    
         
            +
                  precision.to_r
         
     | 
| 
      
 247 
     | 
    
         
            +
                end
         
     | 
| 
      
 248 
     | 
    
         
            +
             
     | 
| 
      
 249 
     | 
    
         
            +
                def clip_latitude(latitude)
         
     | 
| 
      
 250 
     | 
    
         
            +
                  [90.0, [-90.0, latitude].max].min
         
     | 
| 
      
 251 
     | 
    
         
            +
                end
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                def normalize_longitude(longitude)
         
     | 
| 
      
 254 
     | 
    
         
            +
                  until longitude < 180
         
     | 
| 
      
 255 
     | 
    
         
            +
                    longitude -= 360
         
     | 
| 
      
 256 
     | 
    
         
            +
                  end
         
     | 
| 
      
 257 
     | 
    
         
            +
                  until longitude >= -180
         
     | 
| 
      
 258 
     | 
    
         
            +
                    longitude += 360
         
     | 
| 
      
 259 
     | 
    
         
            +
                  end
         
     | 
| 
      
 260 
     | 
    
         
            +
                  longitude
         
     | 
| 
      
 261 
     | 
    
         
            +
                end
         
     | 
| 
      
 262 
     | 
    
         
            +
              end
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,90 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'test/unit'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative '../lib/plus_codes/open_location_code'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class PlusCodesTest < Test::Unit::TestCase
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def setup
         
     | 
| 
      
 7 
     | 
    
         
            +
                @test_data_folder_path = File.join(File.dirname(__FILE__), '..', '..', 'test_data')
         
     | 
| 
      
 8 
     | 
    
         
            +
                @olc = PlusCodes::OpenLocationCode.new
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def test_validity
         
     | 
| 
      
 12 
     | 
    
         
            +
                read_csv_lines('validityTests.csv').each do |line|
         
     | 
| 
      
 13 
     | 
    
         
            +
                  cols = line.split(',')
         
     | 
| 
      
 14 
     | 
    
         
            +
                  code = cols[0]
         
     | 
| 
      
 15 
     | 
    
         
            +
                  is_valid = cols[1] == 'true'
         
     | 
| 
      
 16 
     | 
    
         
            +
                  is_short = cols[2] == 'true'
         
     | 
| 
      
 17 
     | 
    
         
            +
                  is_full = cols[3] == 'true'
         
     | 
| 
      
 18 
     | 
    
         
            +
                  is_valid_olc = @olc.valid?(code)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  is_short_olc = @olc.short?(code)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  is_full_olc = @olc.full?(code)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  result = is_valid_olc == is_valid && is_short_olc == is_short && is_full_olc == is_full
         
     | 
| 
      
 22 
     | 
    
         
            +
                  assert_true(result)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def test_encode_decode
         
     | 
| 
      
 27 
     | 
    
         
            +
                read_csv_lines('encodingTests.csv').each do |line|
         
     | 
| 
      
 28 
     | 
    
         
            +
                  cols = line.split(',')
         
     | 
| 
      
 29 
     | 
    
         
            +
                  code_area = @olc.decode(cols[0])
         
     | 
| 
      
 30 
     | 
    
         
            +
                  if cols[0].index('0')
         
     | 
| 
      
 31 
     | 
    
         
            +
                    code = @olc.encode(cols[1].to_f, cols[2].to_f, cols[0].index('0'))
         
     | 
| 
      
 32 
     | 
    
         
            +
                  else
         
     | 
| 
      
 33 
     | 
    
         
            +
                    code = @olc.encode(cols[1].to_f, cols[2].to_f, cols[0].length - 1)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  assert_equal(cols[0], code)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  assert_true((code_area.south_latitude - cols[3].to_f).abs < 0.001)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  assert_true((code_area.west_longitude - cols[4].to_f).abs < 0.001)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  assert_true((code_area.north_latitude - cols[5].to_f).abs < 0.001)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  assert_true((code_area.east_longitude - cols[6].to_f).abs < 0.001)
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              def test_shorten
         
     | 
| 
      
 44 
     | 
    
         
            +
                read_csv_lines('shortCodeTests.csv').each do |line|
         
     | 
| 
      
 45 
     | 
    
         
            +
                  cols = line.split(',')
         
     | 
| 
      
 46 
     | 
    
         
            +
                  code = cols[0]
         
     | 
| 
      
 47 
     | 
    
         
            +
                  lat = cols[1].to_f
         
     | 
| 
      
 48 
     | 
    
         
            +
                  lng = cols[2].to_f
         
     | 
| 
      
 49 
     | 
    
         
            +
                  short_code = cols[3]
         
     | 
| 
      
 50 
     | 
    
         
            +
                  short = @olc.shorten(code, lat, lng)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  assert_equal(short_code, short)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  expanded = @olc.recover_nearest(short, lat, lng)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  assert_equal(code, expanded)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
                @olc.shorten('9C3W9QCJ+2VX', 60.3701125, 10.202665625)
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              def test_longer_encoding_with_special_case
         
     | 
| 
      
 59 
     | 
    
         
            +
                assert_equal('CFX3X2X2+X2RRRRJ', @olc.encode(90.0, 1.0, 15));
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              def test_exceptions
         
     | 
| 
      
 63 
     | 
    
         
            +
                assert_raise ArgumentError do
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @olc.encode(20, 30, 1)
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
                assert_raise ArgumentError do
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @olc.recover_nearest('9C3W9QCJ-2VX', 51.3708675, -1.217765625)
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
                @olc.recover_nearest('9C3W9QCJ+2VX', 51.3708675, -1.217765625)
         
     | 
| 
      
 70 
     | 
    
         
            +
                assert_raise ArgumentError do
         
     | 
| 
      
 71 
     | 
    
         
            +
                  @olc.decode('sfdg')
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
                assert_raise ArgumentError do
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @olc.shorten('9C3W9Q+', 1, 2)
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
                assert_raise ArgumentError do
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @olc.shorten('9C3W9Q00+', 1, 2)
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
              def test_valid_with_special_case
         
     | 
| 
      
 82 
     | 
    
         
            +
                assert_false(@olc.valid?('3W00CJJJ+'))
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
              def read_csv_lines(csv_file)
         
     | 
| 
      
 86 
     | 
    
         
            +
                f = File.open(File.join(@test_data_folder_path, csv_file), 'r')
         
     | 
| 
      
 87 
     | 
    
         
            +
                f.each_line.lazy.select { |line| line !~ /^\s*#/ }.map { |line| line.chop }
         
     | 
| 
      
 88 
     | 
    
         
            +
              end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,119 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: open-location-code
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.0.1
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Wei-Ming Wu
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2015-10-11 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: bundler
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '1.7'
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: '1.7'
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '10.0'
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '10.0'
         
     | 
| 
      
 41 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 42 
     | 
    
         
            +
              name: test-unit
         
     | 
| 
      
 43 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 44 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 45 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 46 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 47 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 48 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 49 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 50 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 51 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 52 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 53 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 55 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 56 
     | 
    
         
            +
              name: simplecov
         
     | 
| 
      
 57 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 58 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 59 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 60 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 61 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 62 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 63 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 64 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 65 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 66 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 67 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 68 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 69 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 70 
     | 
    
         
            +
              name: yard
         
     | 
| 
      
 71 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 72 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 73 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 74 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 75 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 76 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 77 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 78 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 79 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 80 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 81 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 82 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 83 
     | 
    
         
            +
            description: Ruby implementation of Google Open Location Code(Plus+Codes)
         
     | 
| 
      
 84 
     | 
    
         
            +
            email:
         
     | 
| 
      
 85 
     | 
    
         
            +
            - wnameless@gmail.com
         
     | 
| 
      
 86 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 87 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 88 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 89 
     | 
    
         
            +
            files:
         
     | 
| 
      
 90 
     | 
    
         
            +
            - lib/plus_codes.rb
         
     | 
| 
      
 91 
     | 
    
         
            +
            - lib/plus_codes/code_area.rb
         
     | 
| 
      
 92 
     | 
    
         
            +
            - lib/plus_codes/open_location_code.rb
         
     | 
| 
      
 93 
     | 
    
         
            +
            - test/plus_codes_test.rb
         
     | 
| 
      
 94 
     | 
    
         
            +
            homepage: https://github.com/google/open-location-code
         
     | 
| 
      
 95 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 96 
     | 
    
         
            +
            - Apache License, Version 2.0
         
     | 
| 
      
 97 
     | 
    
         
            +
            metadata: {}
         
     | 
| 
      
 98 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 99 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 100 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 101 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 102 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 103 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 104 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 105 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 106 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 107 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 108 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 109 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 110 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 111 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 112 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 113 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 114 
     | 
    
         
            +
            rubygems_version: 2.4.5
         
     | 
| 
      
 115 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 116 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 117 
     | 
    
         
            +
            summary: Ruby implementation of Google Open Location Code(Plus+Codes)
         
     | 
| 
      
 118 
     | 
    
         
            +
            test_files:
         
     | 
| 
      
 119 
     | 
    
         
            +
            - test/plus_codes_test.rb
         
     |