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 +7 -0
- data/README.md +47 -0
- data/lib/fast-polylines/decoder.rb +42 -0
- data/lib/fast-polylines/encoder.rb +47 -0
- data/lib/fast-polylines/version.rb +3 -0
- data/lib/fast-polylines.rb +5 -0
- data/spec/fast-polylines/decoder_spec.rb +32 -0
- data/spec/fast-polylines/encoder_spec.rb +38 -0
- data/spec/spec_helper.rb +11 -0
- metadata +97 -0
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,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
|
data/spec/spec_helper.rb
ADDED
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
|