fast-polylines 0.1.0

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 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