fast-polylines 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cf28bc2bb47a05f419d1366ecb38d2b33e22af0e
4
+ data.tar.gz: b0fb22fcb016fd2397a4dcc4b7346772a8dcabae
5
+ SHA512:
6
+ metadata.gz: e74e05e39f67996941bf7409d8e7daa526d1578be1e2e3f1f3bbe7dcb564bd22908fd3eaeeb6f17ac3f47e017b7ff54e0554fd3b5a1c91f0a981f08b42c678fe
7
+ data.tar.gz: 5041a5e03079af6a48f50c8f0796382deed04e422cc1721c12edfabcdb19b9bcc5c2ca6bd2505716fffe9dc4d7f43ae61d3a262c5055a888bc0f121e668730ed
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Fast Polylines
2
+
3
+ Implementation of the Google polyline algorithm :
4
+ http://code.google.com/apis/maps/documentation/utilities/polylinealgorithm.html
5
+
6
+ Inspired by Joshua Clayton gem : https://github.com/joshuaclayton/polylines
7
+
8
+ But **at least a 5x faster implementation**.
9
+
10
+ ## Install
11
+
12
+ ```
13
+ gem install fast-polylines
14
+ ```
15
+
16
+ or in Bundler:
17
+ ```
18
+ gem "fast-polylines"
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```
24
+ require 'fast-polylines'
25
+
26
+ FastPolylines::Encoder.encode([[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]])
27
+ # "_p~iF~ps|U_ulLnnqC_mqNvxq`@"
28
+
29
+ FastPolylines::Decoder.decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@")
30
+ # [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]]
31
+ ```
32
+
33
+ ## Advanced usage
34
+
35
+ * With a different precision (default precision of `1e5`) :
36
+
37
+ ```
38
+ FastPolylines::Encoder.encode([[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]], 1e6)
39
+ # "_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI"
40
+
41
+ FastPolylines::Decoder.decode("_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI", 1e6)
42
+ # [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]]
43
+ ```
44
+
45
+ ## License
46
+
47
+ Please see LICENSE
@@ -0,0 +1,42 @@
1
+ module FastPolylines
2
+ ##
3
+ # Provide an interface to decode a Google polyline
4
+ # to an array of lat / lng points
5
+ class Decoder
6
+ ##
7
+ # Decode a polyline to a list of points
8
+ #
9
+ # @param [String] polyline to be decoded
10
+ # @param [Float] precision of the polyline (default 1e5)
11
+ # @return [Array<Array>] A list of lat / lng pairs
12
+ def self.decode(polyline, precision = 1e5)
13
+ coords = []
14
+ acc = ""
15
+ polyline.each_byte do |b|
16
+ acc << b
17
+ next unless b < 0x5f
18
+ coords << acc
19
+ acc = ""
20
+ end
21
+ lat = lng = 0
22
+ coords.each_slice(2).map do |coords_pair|
23
+ lat += decode_number(coords_pair[0], precision)
24
+ lng += decode_number(coords_pair[1], precision)
25
+ [lat, lng]
26
+ end
27
+ end
28
+
29
+ def self.decode_number(string, precision = 1e5)
30
+ result = 1
31
+ shift = 0
32
+ string.each_byte do |b|
33
+ b = b - 63 - 1
34
+ result += b << shift
35
+ shift += 5
36
+ end
37
+ result = (result & 1).nonzero? ? (~result >> 1) : (result >> 1)
38
+ result / precision
39
+ end
40
+ private_class_method :decode_number
41
+ end
42
+ end
@@ -0,0 +1,47 @@
1
+ module FastPolylines
2
+ ##
3
+ # Provides an interface to encode an array of lat / lng points
4
+ # to a Google polyline
5
+ #
6
+ class Encoder
7
+ ##
8
+ # Encode a list of points into a polyline string
9
+ #
10
+ # @param [Array<Array>] points as lat/lng pairs
11
+ # @param [Float] precision of the encoding (default 1e5)
12
+ # @return [String] the encoded polyline
13
+ def self.encode(points, precision = 1e5)
14
+ result = ""
15
+ last_lat = last_lng = 0
16
+ points.each do |point|
17
+ lat = (point[0] * precision).round
18
+ lng = (point[1] * precision).round
19
+ d_lat = lat - last_lat
20
+ d_lng = lng - last_lng
21
+ chunks_lat = encode_number(d_lat)
22
+ chunks_lng = encode_number(d_lng)
23
+ result << chunks_lat << chunks_lng
24
+ last_lat = lat
25
+ last_lng = lng
26
+ end
27
+ result
28
+ end
29
+
30
+ def self.encode_number(num)
31
+ sgn_num = num << 1
32
+ sgn_num = ~sgn_num if num < 0
33
+ encode_unsigned_number(sgn_num)
34
+ end
35
+
36
+ def self.encode_unsigned_number(num)
37
+ encoded = ""
38
+ while num >= 0x20
39
+ encoded << (0x20 | (num & 0x1f)) + 63
40
+ num = num >> 5
41
+ end
42
+ encoded << num + 63
43
+ end
44
+ private_class_method :encode_number
45
+ private_class_method :encode_unsigned_number
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ module FastPolylines
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,5 @@
1
+ require "fast-polylines/encoder"
2
+ require "fast-polylines/decoder"
3
+
4
+ module FastPolylines
5
+ end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+
3
+ describe FastPolylines::Decoder do
4
+ describe ".decode" do
5
+ let(:points) { [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]] }
6
+ context "with default precision" do
7
+ let(:polyline) { "_p~iF~ps|U_ulLnnqC_mqNvxq`@" }
8
+ it "should decode a polyline correctly" do
9
+ expect(described_class.decode(polyline)).to eq points
10
+ end
11
+ it "should perform at least 5x faster than the Polylines gem" do
12
+ expect {
13
+ described_class.decode(polyline)
14
+ }.to perform_faster_than {
15
+ Polylines::Decoder.decode_polyline(polyline)
16
+ }.at_least(5).times
17
+ end
18
+ end
19
+ context "with 1e6 precision" do
20
+ let(:polyline) { "_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI" }
21
+ it "should decode a polyline correctly" do
22
+ expect(described_class.decode(polyline, 1e6)).to eq points
23
+ end
24
+ end
25
+ context "with points that were close together" do
26
+ let(:points) { [[41.35222, -86.04563], [41.35222, -86.04544]] }
27
+ it "should decode a polyline correctly" do
28
+ expect(described_class.decode("krk{FdxdlO?e@")).to eq points
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+
3
+ describe FastPolylines::Encoder do
4
+ describe ".encode" do
5
+ let(:points) { [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]] }
6
+ context "with default precision" do
7
+ let(:polyline) { "_p~iF~ps|U_ulLnnqC_mqNvxq`@" }
8
+ it "should encode points correctly" do
9
+ expect(described_class.encode(points)).to eq polyline
10
+ end
11
+ it "should perform at least 5x faster than the Polylines gem" do
12
+ expect {
13
+ described_class.encode(points)
14
+ }.to perform_faster_than {
15
+ Polylines::Encoder.encode_points(points)
16
+ }.at_least(5).times
17
+ end
18
+ end
19
+ context "with 1e6 precision" do
20
+ let(:polyline) { "_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI" }
21
+ it "should encode points correctly" do
22
+ expect(described_class.encode(points, 1e6)).to eq polyline
23
+ end
24
+ end
25
+ context "with points that were close together" do
26
+ let(:points) { [[41.35222, -86.04563], [41.35222, -86.04544]] }
27
+ it "should encode points correctly" do
28
+ expect(described_class.encode(points)).to eq "krk{FdxdlO?e@"
29
+ end
30
+ end
31
+ context "with possible rounding ambiguity" do
32
+ let(:points) { [[39.13594499,-94.4243478], [39.13558757,-94.4243471]] }
33
+ it "should encode points as Google API do" do
34
+ expect(described_class.encode(points)).to eq "svzmFdgi_QdA?"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,11 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "fast-polylines"
5
+
6
+ require "rspec-benchmark"
7
+ require "polylines"
8
+
9
+ RSpec.configure do |config|
10
+ include RSpec::Benchmark::Matchers
11
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fast-polylines
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Cyrille Courtière
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: polylines
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-benchmark
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.3'
55
+ description:
56
+ email:
57
+ - cyrille@wayzup.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - README.md
63
+ - lib/fast-polylines.rb
64
+ - lib/fast-polylines/decoder.rb
65
+ - lib/fast-polylines/encoder.rb
66
+ - lib/fast-polylines/version.rb
67
+ - spec/fast-polylines/decoder_spec.rb
68
+ - spec/fast-polylines/encoder_spec.rb
69
+ - spec/spec_helper.rb
70
+ homepage: http://github.com/wayzup/fast-polylines
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.6.8
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Fast & easy Google polylines
94
+ test_files:
95
+ - spec/fast-polylines/decoder_spec.rb
96
+ - spec/fast-polylines/encoder_spec.rb
97
+ - spec/spec_helper.rb