grid_square 0.0.1

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.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm gemset use ruby-1.9.3@grid_square --create
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - ruby-head
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ before_install:
8
+ - gem install bundler
9
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+ gem "rspec"
3
+ gem "rspec-given"
data/Gemfile.lock ADDED
@@ -0,0 +1,21 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ rspec (2.11.0)
6
+ rspec-core (~> 2.11.0)
7
+ rspec-expectations (~> 2.11.0)
8
+ rspec-mocks (~> 2.11.0)
9
+ rspec-core (2.11.1)
10
+ rspec-expectations (2.11.2)
11
+ diff-lcs (~> 1.1.3)
12
+ rspec-given (1.5.1)
13
+ rspec (> 1.2.8)
14
+ rspec-mocks (2.11.2)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ rspec
21
+ rspec-given
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # GridSquare
2
+
3
+ Calculate between GridSquare references and latitude/longitude.
4
+
5
+ ```
6
+ grid = GridSquare.new "DN40bi"
7
+
8
+ grid.center
9
+ # => #<Location:0x000001008514d8 @longitude=-109.20833333333333, @latitude=41.6875>
10
+
11
+ grid.width
12
+ # => 0.08333333333333333
13
+
14
+ grid.height
15
+ # => 0.041666666666666664
16
+
17
+ GridSquare.encode(-111.866785, 40.363840).subsquare
18
+ # => "DN40bi"
19
+
20
+ # With arbitrary precision: specify the number of fields
21
+ GridSquare.encode(-111.866785, 40.363840, 10).grid_reference
22
+ # => "DN40BI57XH67OE24bd98"
23
+ ```
24
+
25
+
26
+ # How the US GridSquare System Works
27
+
28
+ Okay in theory the GridSquare system is really complicated. But in
29
+ practice is just base conversion to a number that has a changing
30
+ radix. The first pair of digits are base 18, and the following pairs
31
+ alternate between base 10 and base 24.
32
+
33
+ Here's how it works:
34
+
35
+ The first two letters of a grid reference express the longitude and
36
+ latitude in base 18 (A through R). This pair of letters divides up the
37
+ whole planet into 18 equal "fields", each field being 10 degrees high
38
+ (latitude) and 20 degrees wide (longitude).
39
+
40
+ The next two digits are decimals that divide up each field into 10x10
41
+ "squares" (that's their name; they're obviously not square or even
42
+ rectangular). Squares are 2 degrees wide and 1 degree tall.
43
+
44
+ The next two letters are base 24 (A through X) and divide each square
45
+ into 24x24 "subsquares". Each subsquare is 5 minutes wide and 2.5
46
+ minutes tall.
47
+
48
+ The next two digits are decimals. The official spec calls this the
49
+ "extended subsquare".
50
+
51
+ Officially the locator system stops at extended subsquares (8 digits)
52
+ but unofficially it can be extended by repeating the base 10, base 24
53
+ pairs indefinitely to give precision to the subatomic level and
54
+ beyond. This is of course quite silly as the whole system is based on
55
+ the notion of a spherical earth and the point of the system is to
56
+ economically tell a complete stranger approximately where in the world
57
+ the person they are talking to is located.
58
+
59
+ By convention, the code is capitalized except for the last 2 letters,
60
+ though the first two characters are always capitalized.
61
+
62
+
63
+ # Who Uses this Crap?
64
+
65
+ Ham Radio operators use them to quickly exchange approximate location.
66
+ By convention, shortwave operators give their location in squares
67
+ while VHF and UHF operators give their location in subsquares. A
68
+ square has a precision of about 280km; subsquares give a location to
69
+ within 12km.
70
+
71
+
72
+ # Really?
73
+
74
+ Yes. It even has an amusing name, the Maidenhead Locator System:
75
+ http://en.wikipedia.org/wiki/Maidenhead_Locator_System
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grid_square/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "grid_square"
8
+ gem.version = GridSquare::VERSION
9
+ gem.authors = ["Graham McIntire", "David Brady"]
10
+ gem.email = ["gmcintire@gmail.com"]
11
+ gem.description = %q{Encode/decode between lat/lng locations and US GridSquare references}
12
+ gem.summary = %q{Encode/decode between lat/lng locations and US GridSquare references}
13
+ gem.homepage = "https://github.com/gmcintire/grid_square"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,3 @@
1
+ module GridSquare
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,76 @@
1
+ #require 'location'
2
+ #require 'radix_enumerator'
3
+ #require 'string_ext'
4
+
5
+ class GridSquare
6
+ attr_reader :grid_reference, :origin
7
+
8
+ # location can be a string containing a GS reference or a pair
9
+ # containing longitude and latitude.
10
+ def initialize(grid_reference)
11
+ @grid_reference = grid_reference
12
+ decode
13
+ end
14
+
15
+ def self.encode(longitude, latitude, precision=4 )
16
+ longitude += 180
17
+ latitude += 90
18
+ radixes = RadixEnumerator.new
19
+ grid_reference = ''
20
+ size = Location.new(360.0, 180.0)
21
+ precision.times do
22
+ zero,radix = *radixes.next
23
+
24
+ lng = (radix * longitude / size.longitude).floor
25
+ lat = (radix * latitude / size.latitude).floor
26
+
27
+ grid_reference += "%s%s" % [(zero + lng).chr, (zero + lat).chr]
28
+ size /= radix
29
+ longitude -= lng * size.longitude
30
+ latitude -= lat * size.latitude
31
+ end
32
+
33
+ new grid_reference.downcase_last
34
+ end
35
+
36
+ # Maidenhead locator names
37
+ def field; precision 1; end
38
+ def square; precision 2; end
39
+ def subsquare; precision 3; end
40
+ def extended_subsquare; precision 4; end
41
+
42
+ # Return code to a given number of 2-digit fields
43
+ def precision(num_fields)
44
+ raise IndexError.new "GridSquare.square: insufficient precision to index #{num_fields} fields" unless grid_reference.length >= num_fields * 2
45
+ grid_reference[0...num_fields*2].downcase_last
46
+ end
47
+
48
+ def decode
49
+ radixes = RadixEnumerator.new
50
+ @origin, @size = Location.new(-180.0, -90.0), Location.new(360.0, 180.0)
51
+
52
+ @grid_reference.upcase.chars.each_slice(2) do |lng, lat|
53
+ zero, radix = *radixes.next
54
+ @size /= radix
55
+ @origin += Location.new(@size.longitude * (lng.ord - zero), @size.latitude * (lat.ord - zero))
56
+ end
57
+ end
58
+
59
+ def width; @size.longitude; end
60
+ def height; @size.latitude; end
61
+
62
+ def left; @origin.longitude; end
63
+ def bottom; @origin.latitude; end
64
+ def top; (@origin + @size).latitude; end
65
+ def right; (@origin + @size).longitude; end
66
+
67
+ alias :west :left
68
+ alias :south :bottom
69
+ alias :east :right
70
+ alias :north :top
71
+
72
+ def center
73
+ @origin + @size/2
74
+ end
75
+ end
76
+
data/lib/location.rb ADDED
@@ -0,0 +1,24 @@
1
+ module GridSquare
2
+ class Location
3
+ attr_accessor :longitude, :latitude
4
+ def initialize(longitude, latitude)
5
+ @longitude, @latitude = longitude, latitude
6
+ end
7
+
8
+ def /(scalar)
9
+ Location.new longitude/scalar, latitude/scalar
10
+ end
11
+
12
+ def *(scalar)
13
+ Location.new longitude*scalar, latitude*scalar
14
+ end
15
+
16
+ def +(other)
17
+ Location.new longitude+other.longitude, latitude+other.latitude
18
+ end
19
+
20
+ def -(other)
21
+ Location.new longitude-other.longitude, latitude-other.latitude
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ module GridSquare
2
+ class RadixEnumerator
3
+ def next
4
+ if @enum.nil?
5
+ @enum = ([["0".ord, 10], [?A.ord, 24]]).cycle
6
+ [?A.ord, 18]
7
+ else
8
+ @enum.next
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/string_ext.rb ADDED
@@ -0,0 +1,9 @@
1
+ class String
2
+ def downcase_last
3
+ if length > 4
4
+ self[0...-4].upcase + self[-4..-1].downcase
5
+ else
6
+ upcase
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+ require_relative "../../lib/grid_square"
3
+
4
+ describe GridSquare do
5
+ context "created from field" do
6
+ Given(:grid_square) { GridSquare.new "DN" }
7
+
8
+ Then { grid_square.field.should == "DN" }
9
+ Then { lambda { grid_square.square }.should raise_error(IndexError) }
10
+ Then { lambda { grid_square.subsquare }.should raise_error(IndexError) }
11
+ Then { lambda { grid_square.extended_subsquare.should }.should raise_error(IndexError) }
12
+ Then { lambda { grid_square.precision 10 }.should raise_error(IndexError) }
13
+
14
+ Then { grid_square.west.should be_within(0.00001).of -120.0 }
15
+ Then { grid_square.south.should be_within(0.00001).of 40.0 }
16
+ Then { grid_square.north.should be_within(0.00001).of 50.0 }
17
+ Then { grid_square.east.should be_within(0.00001).of -100.0 }
18
+
19
+ Then { grid_square.width.should be_within(0.00001).of 20.0 }
20
+ Then { grid_square.height.should be_within(0.00001).of 10.0 }
21
+
22
+ Then { grid_square.center.longitude.should be_within(0.00001).of -110.0 }
23
+ Then { grid_square.center.latitude.should be_within(0.00001).of 45.0 }
24
+ end
25
+
26
+ context "created from square" do
27
+ Given(:grid_square) { GridSquare.new "DN41" }
28
+
29
+ Then { grid_square.field.should == "DN" }
30
+ Then { grid_square.square.should == "DN41" }
31
+ Then { lambda { grid_square.subsquare.should == "DN41bi" }.should raise_error(IndexError) }
32
+ Then { lambda { grid_square.extended_subsquare.should }.should raise_error(IndexError) }
33
+ Then { lambda { grid_square.precision 10 }.should raise_error(IndexError) }
34
+
35
+ Then { grid_square.west.should be_within(0.00001).of -112.0 }
36
+ Then { grid_square.south.should be_within(0.00001).of 41.0 }
37
+
38
+ Then { grid_square.width.should be_within(0.00001).of 2.0 }
39
+ Then { grid_square.height.should be_within(0.00001).of 1.0 }
40
+
41
+ Then { grid_square.center.longitude.should be_within(0.00001).of -111.0 }
42
+ Then { grid_square.center.latitude.should be_within(0.00001).of 41.5 }
43
+ end
44
+
45
+ context "created from subsquare" do
46
+ Given(:grid_square) { GridSquare.new "DN41bi" }
47
+
48
+ Then { grid_square.field.should == "DN" }
49
+ Then { grid_square.square.should == "DN41" }
50
+ Then { grid_square.subsquare.should == "DN41bi" }
51
+ Then { lambda { grid_square.extended_subsquare.should }.should raise_error(IndexError) }
52
+ Then { lambda { grid_square.precision 10 }.should raise_error(IndexError) }
53
+
54
+ Then { grid_square.west.should be_within(0.00001).of -111.91666 }
55
+ Then { grid_square.south.should be_within(0.00001).of 41.33333 }
56
+
57
+ Then { grid_square.width.should be_within(0.00001).of 0.083333 }
58
+ Then { grid_square.height.should be_within(0.00001).of 0.041666 }
59
+
60
+ Then { grid_square.center.longitude.should be_within(0.00001).of -111.875 }
61
+ Then { grid_square.center.latitude.should be_within(0.00001).of 41.35416 }
62
+ end
63
+
64
+ context "created from extended subsquare" do
65
+ Given(:grid_square) { GridSquare.new "DN41bi63" }
66
+
67
+ Then { grid_square.field.should == "DN" }
68
+ Then { grid_square.square.should == "DN41" }
69
+ Then { grid_square.subsquare.should == "DN41bi" }
70
+ Then { grid_square.extended_subsquare.should == "DN41bi63" }
71
+ Then { lambda { grid_square.precision 10 }.should raise_error(IndexError) }
72
+
73
+ Then { grid_square.west.should be_within(0.00001).of -111.86666 }
74
+ Then { grid_square.south.should be_within(0.00001).of 41.34583 }
75
+
76
+ Then { grid_square.width.should be_within(0.00001).of 0.0083333 }
77
+ Then { grid_square.height.should be_within(0.00001).of 0.0041666 }
78
+
79
+ Then { grid_square.center.longitude.should be_within(0.00001).of -111.8625 }
80
+ Then { grid_square.center.latitude.should be_within(0.00001).of 41.34792 }
81
+ end
82
+
83
+ context "proper field casing" do
84
+ Given(:upper) { GridSquare.new "DN41BI83QR" }
85
+ Given(:lower) { GridSquare.new "dn41bi83qr" }
86
+
87
+ Then { upper.field.should == "DN" }
88
+ Then { upper.square.should == "DN41" }
89
+ Then { upper.subsquare.should == "DN41bi" }
90
+ Then { upper.precision(4).should == "DN41bi83" }
91
+
92
+ Then { lower.field.should == "DN" }
93
+ Then { lower.square.should == "DN41" }
94
+ Then { lower.subsquare.should == "DN41bi" }
95
+ Then { lower.precision(4).should == "DN41bi83" }
96
+ end
97
+
98
+ context "created from a ridiculously long GridSquare code" do
99
+ Given(:ludicrous) { GridSquare.new "RN91SK59MC07FQ84VK31SM03PF57XS16DU32MS24LX34EI48RQ28CS09MA87VO18GS16WK64JL24XF21KJ12FO37RJ73QL28IU39AS55HC86SW74JD70VQ61SF52QA21SD28GB60HT40SN34VG10NP11TP27CR36OA76LD28TI36JE73TF03OT41UH60IX96IH80HX45KC53SJ57AF07HQ38NI74WT84LP33UQ34OP06TV69IA96LB80VW57MV04SJ88AV74GO73JW26BF53DE74BH31EV57WP25WO98IL47BT13AD44WC66BL08IA01KN55FV05EI50KM77HV72LF21NG51XI69QW28PO80FW77AI36BE19ID25JR96KA39DA28LK83PL86TP85AH21LB31NF52BQ06WJ64AQ05XK16RJ16SP96KE85BU61BS68OK10HC39QH86XB51PO62RA27JH93PM08NF46XS98HN76LJ55KQ68AQ34JR96OF09DJ13NP76BP09AP83MK60SE83FX63OS03AG31HA46KB00QF06LU66DX71PH65WU26IF88AO21LE62DI34GF23hc34" }
100
+ Then { ludicrous.field.should == "RN" }
101
+ Then { ludicrous.square.should == "RN91" }
102
+ Then { ludicrous.subsquare.should == "RN91sk" }
103
+ Then { ludicrous.extended_subsquare.should == "RN91sk59" }
104
+ Then { lambda { ludicrous.precision 301 }.should raise_error(IndexError) }
105
+ end
106
+
107
+ describe ".encode" do
108
+ Given(:grid_square) { GridSquare.encode -111.866785, 40.363840 }
109
+
110
+ Then { grid_square.grid_reference.should == "DN40bi57" }
111
+
112
+ context "with precision" do
113
+ Given(:grid_square) { GridSquare.encode -111.866785, 40.363840, 10 }
114
+
115
+ Then { grid_square.grid_reference.should == "DN40BI57XH67OE24bd98" }
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require_relative '../../lib/location'
3
+
4
+ module GridSquare
5
+ describe Location do
6
+ # We can use Locations to represent exact points...
7
+ Given(:origin) { Location.new 0.0, 0.0 }
8
+
9
+ # Or spans/sizes
10
+ Given(:window) { Location.new -180.0, -90.0 }
11
+
12
+ Then { window.longitude.should be_within(0.000001).of -180.0 }
13
+ Then { window.latitude.should be_within(0.000001).of -90.0 }
14
+
15
+ context "translation +" do
16
+ Given(:new_origin) { origin + Location.new(50.0, 40.0) }
17
+ Then { new_origin.longitude.should be_within(0.000001).of 50.0 }
18
+ Then { new_origin.latitude.should be_within(0.000001).of 40.0 }
19
+ end
20
+
21
+ context "translation -" do
22
+ Given(:new_origin) { origin - Location.new(50.0, 40.0) }
23
+ Then { new_origin.longitude.should be_within(0.000001).of -50.0 }
24
+ Then { new_origin.latitude.should be_within(0.000001).of -40.0 }
25
+ end
26
+
27
+ context "scaling *" do
28
+ Given(:new_window) { window * 2 }
29
+ Then { new_window.longitude.should be_within(0.000001).of -360.0 }
30
+ Then { new_window.latitude.should be_within(0.000001).of -180.0 }
31
+ end
32
+
33
+ context "scaling /" do
34
+ Given(:new_window) { window / 2 }
35
+ Then { new_window.longitude.should be_within(0.000001).of -90.0 }
36
+ Then { new_window.latitude.should be_within(0.000001).of -45.0 }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require_relative '../../lib/radix_enumerator'
3
+
4
+ module GridSquare
5
+ describe RadixEnumerator do
6
+ Given(:radix) { RadixEnumerator.new }
7
+
8
+ Then {
9
+ radix.next[0].should == ?A.ord
10
+ radix.next[0].should == "0".ord
11
+ radix.next[0].should == ?A.ord
12
+ radix.next[0].should == "0".ord
13
+ radix.next[0].should == ?A.ord
14
+ radix.next[0].should == "0".ord
15
+ radix.next[0].should == ?A.ord
16
+ radix.next[0].should == "0".ord
17
+ radix.next[0].should == ?A.ord
18
+ }
19
+
20
+ Then {
21
+ radix.next[1].should == 18
22
+ radix.next[1].should == 10
23
+ radix.next[1].should == 24
24
+ radix.next[1].should == 10
25
+ radix.next[1].should == 24
26
+ radix.next[1].should == 10
27
+ radix.next[1].should == 24
28
+ radix.next[1].should == 10
29
+ radix.next[1].should == 24
30
+ }
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require_relative "../../lib/string_ext"
3
+
4
+ describe String do
5
+ describe "#downcase_last" do
6
+ context "field" do
7
+ Given(:upper) { "DN" }
8
+ Given(:lower) { "dn" }
9
+
10
+ Then { upper.downcase_last.should == "DN" }
11
+ Then { lower.downcase_last.should == "DN" }
12
+ end
13
+
14
+ context "ending in digits" do
15
+ Given(:upper) { "DN41BI83" }
16
+ Given(:lower) { "dn41bi83" }
17
+
18
+ Then { upper.downcase_last.should == "DN41bi83" }
19
+ Then { lower.downcase_last.should == "DN41bi83" }
20
+ end
21
+
22
+ context "subsquare or longer" do
23
+ Given(:upper) { "DN41BI83QR" }
24
+ Given(:lower) { "dn41bi83qr" }
25
+
26
+ Then { upper.downcase_last.should == "DN41BI83qr" }
27
+ Then { lower.downcase_last.should == "DN41BI83qr" }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1 @@
1
+ require "rspec-given"
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grid_square
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Graham McIntire
9
+ - David Brady
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-11-16 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Encode/decode between lat/lng locations and US GridSquare references
16
+ email:
17
+ - gmcintire@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .rspec
23
+ - .rvmrc
24
+ - .travis.yml
25
+ - Gemfile
26
+ - Gemfile.lock
27
+ - README.md
28
+ - Rakefile
29
+ - grid_square.gemspec
30
+ - lib/grid_square.rb
31
+ - lib/grid_square/version.rb
32
+ - lib/location.rb
33
+ - lib/radix_enumerator.rb
34
+ - lib/string_ext.rb
35
+ - spec/lib/grid_square_spec.rb
36
+ - spec/lib/location_spec.rb
37
+ - spec/lib/radix_enumerator_spec.rb
38
+ - spec/lib/string_ext_spec.rb
39
+ - spec/spec_helper.rb
40
+ homepage: https://github.com/gmcintire/grid_square
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.23
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Encode/decode between lat/lng locations and US GridSquare references
64
+ test_files:
65
+ - spec/lib/grid_square_spec.rb
66
+ - spec/lib/location_spec.rb
67
+ - spec/lib/radix_enumerator_spec.rb
68
+ - spec/lib/string_ext_spec.rb
69
+ - spec/spec_helper.rb