Skip to content

Commit

Permalink
Encoding and decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuaclayton committed Jan 31, 2011
1 parent 64ce6e8 commit 2c92e19
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 1 deletion.
24 changes: 24 additions & 0 deletions Gemfile.lock
@@ -0,0 +1,24 @@
PATH
remote: .
specs:
polylines (0.0.1)

GEM
remote: http://rubygems.org/
specs:
diff-lcs (1.1.2)
rspec (2.4.0)
rspec-core (~> 2.4.0)
rspec-expectations (~> 2.4.0)
rspec-mocks (~> 2.4.0)
rspec-core (2.4.0)
rspec-expectations (2.4.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.4.0)

PLATFORMS
ruby

DEPENDENCIES
polylines!
rspec (= 2.4.0)
10 changes: 10 additions & 0 deletions Rakefile
@@ -1,2 +1,12 @@
require "rake"
require "rake/tasklib"
require 'bundler' require 'bundler'
Bundler::GemHelper.install_tasks Bundler::GemHelper.install_tasks

Bundler.require

require "rspec/core/rake_task"

RSpec::Core::RakeTask.new(:rspec)

task :default => [:rspec]
3 changes: 2 additions & 1 deletion lib/polylines.rb
@@ -1,3 +1,4 @@
module Polylines module Polylines
# Your code goes here... autoload :Encoder, "polylines/encoder"
autoload :Decoder, "polylines/decoder"
end end
84 changes: 84 additions & 0 deletions lib/polylines/decoder.rb
@@ -0,0 +1,84 @@
module Polylines
class Decoder
def self.decode_polyline(polyline)
set = []

points_with_deltas = polyline.split(//).inject([]) do |charset, char|
set << char

if ((char[0] - 63) & 0x20) == 0
charset << set.join
set = []
end

charset
end.map {|set| decode(set) }

points = [[points_with_deltas.shift, points_with_deltas.shift]]

while points_with_deltas.any?
new_latitude = points.last[0] + points_with_deltas.shift
new_longitude = points.last[1] + points_with_deltas.shift
points << [new_latitude, new_longitude]
end

points
end

def self.decode(string)
decimal_values = step_11(string)
decimal_values = step_10(decimal_values)
xord_values = step_8(decimal_values)
five_bit_chunks = step_7(xord_values)
binary_string = step_6(five_bit_chunks)

negative = binary_string[-1, 1] == "1"

number = binary_string.to_i(2)
number = step_5(number) if negative
number = step_4(number)
number = step_3(number) if negative
step_2(number)
end

def self.step_2(number)
number.to_f/1e5
end

def self.step_3(number)
number -= 1
number = ~number
number *= -1
end

def self.step_4(number)
number >> 1
end

def self.step_5(number)
~number
end

def self.step_6(five_bit_chunks)
five_bit_chunks.map do |chunk|
"%05b" % chunk
end.join
end

def self.step_7(five_bit_chunks)
five_bit_chunks.reverse
end

def self.step_8(decimal_values)
decimal_values[0..-2].map {|value| 0x20 ^ value } << decimal_values.last
end

def self.step_10(values)
values.map {|value| value - 63 }
end

def self.step_11(string)
string.split(//).map {|char| char[0] }
end
end
end
72 changes: 72 additions & 0 deletions lib/polylines/encoder.rb
@@ -0,0 +1,72 @@
module Polylines
class Encoder
def self.encode_points(points)
delta_latitude, delta_longitude = 0, 0

points.inject([]) do |polyline, (latitude, longitude)|
polyline << encode(latitude - delta_latitude)
polyline << encode(longitude - delta_longitude)
delta_latitude, delta_longitude = latitude, longitude
polyline
end.join
end

def self.encode(number)
negative = number < 0
number = step_2(number)
number = step_3(number) if negative
number = step_4(number)
number = step_5(number) if negative

five_bit_chunks = step_6(number)
five_bit_chunks = step_7(five_bit_chunks)
ord_values = step_8(five_bit_chunks)
decimal_values = step_10(ord_values)

step_11(decimal_values)
end

def self.step_2(number)
(number * 1e5).round
end

def self.step_3(number)
number *= -1
number = ~number
number += 1
end

def self.step_4(number)
number << 1
end

def self.step_5(number)
~number
end

def self.step_6(number)
[].tap do |numbers|
while number > 0 do
numbers.unshift(number & 0x1f)
number = number >> 5
end
end
end

def self.step_7(five_bit_chunks)
five_bit_chunks.reverse
end

def self.step_8(five_bit_chunks)
five_bit_chunks[0..-2].map {|chunk| 0x20 | chunk } << five_bit_chunks.last
end

def self.step_10(values)
values.map {|value| value + 63 }
end

def self.step_11(values)
values.map(&:chr).join
end
end
end
2 changes: 2 additions & 0 deletions polylines.gemspec
Expand Up @@ -15,4 +15,6 @@ Gem::Specification.new do |s|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"] s.require_paths = ["lib"]

s.add_development_dependency("rspec", "2.4.0")
end end
16 changes: 16 additions & 0 deletions spec/polylines/decoder_spec.rb
@@ -0,0 +1,16 @@
require "spec_helper"

describe Polylines::Decoder, ".decode" do
it "decodes a single point" do
Polylines::Decoder.decode("`~oia@").should be_within(0.00001).of(-179.9832104)
end
end

describe Polylines::Decoder, ".decode_polyline" do
let(:polyline) { "_p~iF~ps|U_ulLnnqC_mqNvxq`@" }
let(:points) { [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]] }

it "decodes a polyline correctly" do
Polylines::Decoder.decode_polyline(polyline).should == points
end
end
16 changes: 16 additions & 0 deletions spec/polylines/encoder_spec.rb
@@ -0,0 +1,16 @@
require "spec_helper"

describe Polylines::Encoder, ".encode" do
it "encodes a single point" do
Polylines::Encoder.encode(-179.9832104).should == "`~oia@"
end
end

describe Polylines::Encoder, ".encode_points" do
let(:polyline) { "_p~iF~ps|U_ulLnnqC_mqNvxq`@" }
let(:points) { [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]] }

it "encodes points correctly" do
Polylines::Encoder.encode_points(points).should == polyline
end
end
7 changes: 7 additions & 0 deletions spec/spec_helper.rb
@@ -0,0 +1,7 @@
require "rubygems"
require "bundler/setup"

require "polylines"
#
# RSpec.configure do |config|
# end

0 comments on commit 2c92e19

Please sign in to comment.