]> git.openstreetmap.org Git - rails.git/blobdiff - lib/short_link.rb
Fix feature request GitHub issue template
[rails.git] / lib / short_link.rb
index b91d7e569dddc57d12e3b3fe02d11df7aea10e82..424e85c1095999e781147f35610193b36ade59e0 100644 (file)
@@ -5,75 +5,85 @@
 # Each character encodes 3 bits of x and 3 of y, so there are extra characters
 # tacked on the end to make the zoom levels "work".
 module ShortLink
-
   # array of 64 chars to encode 6 bits. this is almost like base64 encoding, but
-  # the symbolic chars are different, as base64's + and / aren't very 
+  # the symbolic chars are different, as base64's + and / aren't very
   # URL-friendly.
-  ARRAY = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['_','@']
+  ARRAY = ("A".."Z").to_a + ("a".."z").to_a + ("0".."9").to_a + ["_", "~"]
+
+  class << self
+    ##
+    # Given a string encoding a location, returns the [lon, lat, z] tuple of that
+    # location.
+    def decode(str)
+      x = 0
+      y = 0
+      z = 0
+      z_offset = 0
+
+      # keep support for old shortlinks which use the @ character, now
+      # replaced by the ~ character because twitter is horribly broken
+      # and we can't have that.
+      str.tr!("@", "~")
 
-  ##
-  # Given a string encoding a location, returns the [lon, lat, z] tuple of that 
-  # location.
-  def self.decode(str)
-    x = 0
-    y = 0
-    z = 0
-    z_offset = 0
+      str.each_char do |c|
+        t = ARRAY.index c
+        if t.nil?
+          z_offset -= 1
+        else
+          3.times do
+            x <<= 1
+            x |= 1 unless (t & 32).zero?
+            t <<= 1
 
-    str.each_char do |c|
-      t = ARRAY.index c
-      if t.nil?
-        z_offset -= 1
-      else
-        3.times do
-          x <<= 1; x = x | 1 unless (t & 32).zero?; t <<= 1
-          y <<= 1; y = y | 1 unless (t & 32).zero?; t <<= 1
+            y <<= 1
+            y |= 1 unless (t & 32).zero?
+            t <<= 1
+          end
+          z += 3
         end
-        z += 3
       end
+      # pack the coordinates out to their original 32 bits.
+      x <<= (32 - z)
+      y <<= (32 - z)
+
+      # project the parameters back to their coordinate ranges.
+      [(x * 360.0 / (2**32)) - 180.0,
+       (y * 180.0 / (2**32)) - 90.0,
+       z - 8 - (z_offset % 3)]
     end
-    # pack the coordinates out to their original 32 bits.
-    x <<= (32 - z)
-    y <<= (32 - z)
 
-    # project the parameters back to their coordinate ranges.
-    [(x * 360.0 / 2**32) - 180.0, 
-     (y * 180.0 / 2**32) - 90.0, 
-     z - 8 - (z_offset % 3)]
-  end
+    ##
+    # given a location and zoom, return a short string representing it.
+    def encode(lon, lat, z)
+      code = interleave_bits(((lon + 180.0) * (2**32) / 360.0).to_i,
+                             ((lat + 90.0) * (2**32) / 180.0).to_i)
+      str = ""
+      # add eight to the zoom level, which approximates an accuracy of
+      # one pixel in a tile.
+      ((z + 8) / 3.0).ceil.times do |i|
+        digit = (code >> (58 - (6 * i))) & 0x3f
+        str << ARRAY[digit]
+      end
+      # append characters onto the end of the string to represent
+      # partial zoom levels (characters themselves have a granularity
+      # of 3 zoom levels).
+      ((z + 8) % 3).times { str << "-" }
 
-  ##
-  # given a location and zoom, return a short string representing it.
-  def self.encode(lon, lat, z)
-    code = interleave_bits(((lon + 180.0) * 2**32 / 360.0).to_i, 
-                           ((lat +  90.0) * 2**32 / 180.0).to_i)
-    str = ""
-    # add eight to the zoom level, which approximates an accuracy of
-    # one pixel in a tile.
-    ((z + 8)/3.0).ceil.times do |i|
-      digit = (code >> (58 - 6 * i)) & 0x3f
-      str << ARRAY[digit]
+      str
     end
-    # append characters onto the end of the string to represent
-    # partial zoom levels (characters themselves have a granularity
-    # of 3 zoom levels).
-    ((z + 8) % 3).times { str << "-" }
-    
-    return str
-  end
 
-  private
-  
-  ##
-  # interleaves the bits of two 32-bit numbers. the result is known
-  # as a Morton code.
-  def self.interleave_bits(x, y)
-    c = 0
-    31.downto(0) do |i|
-      c = (c << 1) | ((x >> i) & 1)
-      c = (c << 1) | ((y >> i) & 1)
+    private
+
+    ##
+    # interleaves the bits of two 32-bit numbers. the result is known
+    # as a Morton code.
+    def interleave_bits(x, y)
+      c = 0
+      31.downto(0) do |i|
+        c = (c << 1) | ((x >> i) & 1)
+        c = (c << 1) | ((y >> i) & 1)
+      end
+      c
     end
-    c
   end
-
 end