osgb 0.2.0 → 0.3.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.
- data/README.markdown +51 -0
- data/lib/osgb.rb +11 -2
- data/lib/osgb/gridref.rb +39 -49
- data/lib/osgb/helmert.rb +1 -1
- data/lib/osgb/point.rb +114 -0
- data/lib/osgb/string_conversions.rb +105 -11
- data/spec/point_spec.rb +26 -0
- data/spec/string_spec.rb +35 -7
- metadata +7 -5
- data/README.md +0 -51
data/README.markdown
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# OSGB
|
2
|
+
|
3
|
+
Osgb is a library that converts between British (and Irish) grid references and latitude and longitude co-ordinates. It is theoretically precise to about 1m, which is to say it's good for WGS84 and most GPS use but not up to surveying with ITRS or ETRS89. So don't do that.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
In <b>Rails 3</b>, add this to your Gemfile and run `bundle install`.
|
8
|
+
|
9
|
+
gem "osgb"
|
10
|
+
|
11
|
+
In <b>Rails 2</b>, add this to your environment.rb file.
|
12
|
+
|
13
|
+
config.gem "osgb"
|
14
|
+
|
15
|
+
Alternatively, you can install it as a plugin.
|
16
|
+
|
17
|
+
rails plugin install git://github.com/spanner/osgb.git
|
18
|
+
|
19
|
+
## Status
|
20
|
+
|
21
|
+
Early days: activerecord interface hasn't settled down, some refactoring likely, bugs entirely possible. The basic algorithms are ancient and sound, though.
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
You don't need to make any explicit reference to the gem. It adds conversion methods to the String class:
|
26
|
+
|
27
|
+
"SD28687846".is_gridref? # -> true
|
28
|
+
"SD28687846".to_latlng # -> [54.196763, -3.093320]
|
29
|
+
"SD28687846".to_wgs84 # -> [54.196915, -3.094684]
|
30
|
+
"1.056789, 55.98978607".is_latlng? # -> true
|
31
|
+
|
32
|
+
and provides some (tentative) help for your ActiveRecord classes:
|
33
|
+
|
34
|
+
class Checkpoint < ActiveRecord::Base
|
35
|
+
has_gridref :lat => 'lat',
|
36
|
+
:lng => 'lng',
|
37
|
+
:gridref => 'gridref',
|
38
|
+
:validation => false,
|
39
|
+
:converstion => true
|
40
|
+
|
41
|
+
The :lat, :lng and :gridref keys should pass in the names of the relevant columns if they don't match these defaults.
|
42
|
+
|
43
|
+
## Bugs and features
|
44
|
+
|
45
|
+
[Github issues](http://github.com/spanner/osgb/issues) please, or for little things an email or github message is fine.
|
46
|
+
|
47
|
+
## Author & Copyright
|
48
|
+
|
49
|
+
Copyright 2008-2011 Will at spanner.org.
|
50
|
+
|
51
|
+
Released under the same terms as Ruby
|
data/lib/osgb.rb
CHANGED
@@ -2,6 +2,7 @@ require 'osgb/angle_conversions' # converts degrees to radians and
|
|
2
2
|
require 'osgb/ellipsoid' # standard approximations to the squashed-circle shape of the earth
|
3
3
|
require 'osgb/projection' # the geometrical distortions required by a map projection
|
4
4
|
require 'osgb/helmert' # 3d transformation algorithm for mapping between cartesian and ellipsoidal polar coordinates
|
5
|
+
require 'osgb/point' # versatile coordinate pair
|
5
6
|
require 'osgb/gridref' # parse grid references and returns lat/long pairs
|
6
7
|
require 'osgb/string_conversions' # add conversion methods to String
|
7
8
|
require 'osgb/railtie' if defined? Rails # add useful methods to ActiveRecord
|
@@ -21,6 +22,14 @@ Osgb::Projection.new :utm29, :scale => 0.9996, :phi0 => 0, :lambda0 => -9, :e0 =
|
|
21
22
|
Osgb::Projection.new :utm30, :scale => 0.9996, :phi0 => 0, :lambda0 => -3, :e0 => 500000, :n0 => 0, :ellipsoid => :utm
|
22
23
|
Osgb::Projection.new :utm31, :scale => 0.9996, :phi0 => 0, :lambda0 => 3, :e0 => 500000, :n0 => 0, :ellipsoid => :utm
|
23
24
|
|
24
|
-
# the Helmert
|
25
|
+
# the Helmert matrices used to translate from osgb36 to wgs84 and vice versa
|
25
26
|
|
26
|
-
Osgb::Helmert.new :
|
27
|
+
Osgb::Helmert.new :osgb36_to_wgs84, :tx => 446.448, :ty => -125.157, :tz => 542.060, :rx => 0.1502, :ry => 0.2470, :rz => 0.8421, :s => -20.4894
|
28
|
+
Osgb::Helmert.new :wgs84_to_osgb36, :tx => -446.448, :ty => 125.157, :tz => -542.060, :rx => -0.1502, :ry => -0.2470, :rz => -0.8421, :s => 20.4894
|
29
|
+
|
30
|
+
# Housekeeping
|
31
|
+
|
32
|
+
module Osgb
|
33
|
+
class TransformationError < StandardError; end
|
34
|
+
class ConfigurationError < StandardError; end
|
35
|
+
end
|
data/lib/osgb/gridref.rb
CHANGED
@@ -4,22 +4,29 @@ module Osgb
|
|
4
4
|
# with help from CPAN module Geography::NationalGrid by and (c) P Kent
|
5
5
|
|
6
6
|
class Gridref
|
7
|
-
|
7
|
+
|
8
|
+
# maps OS letter codes onto their coordinates in the master grid
|
9
|
+
OS_TILES = {
|
8
10
|
:a => [0,4], :b => [1,4], :c => [2,4], :d => [3,4], :e => [4,4],
|
9
11
|
:f => [0,3], :g => [1,3], :h => [2,3], :j => [3,3], :k => [4,3],
|
10
12
|
:l => [0,2], :m => [1,2], :n => [2,2], :o => [3,2], :p => [4,2],
|
11
13
|
:q => [0,1], :r => [1,1], :s => [2,1], :t => [3,1], :u => [4,1],
|
12
14
|
:v => [0,0], :w => [1,0], :x => [2,0], :y => [3,0], :z => [4,0],
|
13
15
|
}
|
14
|
-
|
15
|
-
|
16
|
+
|
17
|
+
# the offset makes all coordinates positive and <1000km
|
18
|
+
FALSE_ORIGIN = {:e => 2, :n => 1}
|
19
|
+
|
20
|
+
# a shorter grid ref denotes a larger square
|
21
|
+
SQUARE_SIZE = [nil, 10000, 1000, 100, 10, 1]
|
16
22
|
|
17
|
-
attr_accessor :gridref, :projection, :ellipsoid, :
|
23
|
+
attr_accessor :gridref, :projection, :ellipsoid, :options, :precision
|
18
24
|
|
25
|
+
@@default_datum = :osgb36
|
19
26
|
@@iteration_ceiling = 1000
|
20
27
|
@@defaults = {
|
21
28
|
:projection => :gb, # mercator projection of input gridref. Can be any projection name: usually :ie or :gb
|
22
|
-
:precision => 6
|
29
|
+
:precision => 6, # decimal places in the output lat/long
|
23
30
|
}
|
24
31
|
|
25
32
|
class << self
|
@@ -27,7 +34,7 @@ module Osgb
|
|
27
34
|
@@iteration_ceiling
|
28
35
|
end
|
29
36
|
end
|
30
|
-
|
37
|
+
|
31
38
|
def initialize(string, options={})
|
32
39
|
raise ArgumentError, "invalid grid reference string '#{string}'." unless string.is_gridref?
|
33
40
|
options = @@defaults.merge(options)
|
@@ -48,16 +55,16 @@ module Osgb
|
|
48
55
|
end
|
49
56
|
|
50
57
|
def resolution
|
51
|
-
digits.length / 2
|
58
|
+
@resolution ||= digits.length / 2
|
52
59
|
end
|
53
60
|
|
54
61
|
def offsets
|
55
62
|
if tile
|
56
|
-
major =
|
57
|
-
minor =
|
63
|
+
major = OS_TILES[tile[0,1].downcase.to_sym ]
|
64
|
+
minor = OS_TILES[tile[1,1].downcase.to_sym]
|
58
65
|
@offset ||= {
|
59
|
-
:e => (500000 * (major[0] -
|
60
|
-
:n => (500000 * (major[1] -
|
66
|
+
:e => (500000 * (major[0] - FALSE_ORIGIN[:e])) + (100000 * minor[0]),
|
67
|
+
:n => (500000 * (major[1] - FALSE_ORIGIN[:n])) + (100000 * minor[1])
|
61
68
|
}
|
62
69
|
else
|
63
70
|
{ :e => 0, :n => 0 }
|
@@ -65,40 +72,40 @@ module Osgb
|
|
65
72
|
end
|
66
73
|
|
67
74
|
def easting
|
68
|
-
@east ||= offsets[:e] + digits[0, resolution].to_i *
|
75
|
+
@east ||= offsets[:e] + digits[0, resolution].to_i * SQUARE_SIZE[resolution]
|
69
76
|
end
|
70
77
|
|
71
78
|
def northing
|
72
|
-
@north ||= offsets[:n] + digits[resolution, resolution].to_i *
|
79
|
+
@north ||= offsets[:n] + digits[resolution, resolution].to_i * SQUARE_SIZE[resolution]
|
73
80
|
end
|
74
81
|
|
75
|
-
def lat
|
76
|
-
|
82
|
+
def lat(datum=nil)
|
83
|
+
to_latlng(datum).lat
|
77
84
|
end
|
78
85
|
|
79
|
-
def lng
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
def round(value)
|
84
|
-
if value.method(:round).arity == 0
|
85
|
-
multiplier = 10**precision
|
86
|
-
(value * multiplier).round.to_f / multiplier
|
87
|
-
else
|
88
|
-
value.round(precision)
|
89
|
-
end
|
86
|
+
def lng(datum=nil)
|
87
|
+
to_latlng(datum).lng
|
90
88
|
end
|
91
89
|
|
92
90
|
def to_s
|
93
91
|
gridref.to_s
|
94
92
|
end
|
95
93
|
|
96
|
-
|
97
|
-
|
94
|
+
# Returns an Osgb::Point corresponding to this grid reference and lying on the specified datum.
|
95
|
+
# We default to WGS84 since that is the representation most likely to be useful.
|
96
|
+
#
|
97
|
+
def to_latlng(datum=nil)
|
98
|
+
datum ||= :wgs84
|
99
|
+
point.transform_to(datum)
|
98
100
|
end
|
99
101
|
|
100
|
-
|
101
|
-
|
102
|
+
private
|
103
|
+
|
104
|
+
# Returns an Osgb::Point corresponding to this grid reference. Since it is not yet transformed,
|
105
|
+
# the point will lie on the native OSGB36 datum.
|
106
|
+
#
|
107
|
+
def point
|
108
|
+
unless @point
|
102
109
|
# variable names correspond roughly to symbols in the OS algorithm, lowercased:
|
103
110
|
# n0 = northing of true origin
|
104
111
|
# e0 = easting of true origin
|
@@ -168,27 +175,10 @@ module Osgb
|
|
168
175
|
phi = phi - vii*(d**2) + viii*(d**4) - ix*(d**6)
|
169
176
|
lambda = l0 + x*d - xi*(d**3) + xii*(d**5) - xiia*(d**7)
|
170
177
|
|
171
|
-
|
172
|
-
# if a different output datum is required, the helmert transformation remaps the coordinates onto a new globe
|
173
|
-
|
174
|
-
if datum && datum != :osgb36
|
175
|
-
target_ellipsoid = Osgb::Ellipsoid[datum]
|
176
|
-
|
177
|
-
if helmert = Osgb::Helmert[datum]
|
178
|
-
cartesian_coordinates = ellipsoid.polar_to_cartesian(phi, lambda)
|
179
|
-
transformed = helmert.transform(*cartesian_coordinates)
|
180
|
-
phi, lambda = target_ellipsoid.cartesian_to_polar(*transformed)
|
181
|
-
else
|
182
|
-
raise RuntimeError, "Missing ellipsoid or helmert transformation for #{datum}"
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
@coordinates = {:lat => phi, :lng => lambda}
|
178
|
+
@point = Osgb::Point.new(phi.to_degrees, lambda.to_degrees, :osgb36, precision)
|
187
179
|
end
|
188
|
-
@
|
180
|
+
@point
|
189
181
|
end
|
190
|
-
|
191
|
-
private
|
192
182
|
|
193
183
|
def sec(radians)
|
194
184
|
1 / Math.cos(radians)
|
data/lib/osgb/helmert.rb
CHANGED
data/lib/osgb/point.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module Osgb
|
2
|
+
class Point
|
3
|
+
# A coordinate pair on a specified ellipsoid,
|
4
|
+
# easy to use either in string or array context
|
5
|
+
# and able to perform basic distance and equivalence.
|
6
|
+
# calculations.
|
7
|
+
|
8
|
+
attr_writer :lat, :lng
|
9
|
+
attr_accessor :datum, :precision
|
10
|
+
|
11
|
+
# Usage:
|
12
|
+
# Osgb::Point.new(lat[float], lng[float], datum[symbol], precision[integer])
|
13
|
+
#
|
14
|
+
# Default datum is :osgb36. For most web use you will be using WGS84 points, since
|
15
|
+
# that's the standard for most GPS applications and used by google maps:
|
16
|
+
#
|
17
|
+
# Osgb::Point.new(54.196915, -3.094684, :wgs84)
|
18
|
+
# Osgb::Point.new(54.196763, -3.093320).transform_to(:wgs84)
|
19
|
+
#
|
20
|
+
def initialize(lat, lng, datum=nil, precision=nil)
|
21
|
+
@lat = lat.to_f
|
22
|
+
@lng = lng.to_f
|
23
|
+
@datum = datum || :osgb36
|
24
|
+
@precision = precision || 6
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the latitude of the point, rounded to the specified precision
|
28
|
+
#
|
29
|
+
def lat
|
30
|
+
round(@lat)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the longitude of the point, rounded to the specified precision
|
34
|
+
#
|
35
|
+
def lng
|
36
|
+
round(@lng)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns true if the coordinates are both specified and within the acceptable range.
|
40
|
+
#
|
41
|
+
def valid?
|
42
|
+
lat && lng && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the point as a "lat,lng" string.
|
46
|
+
def to_s
|
47
|
+
"#{lat},#{lng}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the point as a [lat,lng] array.
|
51
|
+
def to_a
|
52
|
+
[lat, lng]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Remaps the point onto another datum. If you're turning OS grid references into GPS coordinates you
|
56
|
+
# have to remap from OSGB36 to WGS84:
|
57
|
+
#
|
58
|
+
# point = "SD28687846".to_latlng.transform_to(:wgs84)
|
59
|
+
#
|
60
|
+
# or more concisely:
|
61
|
+
#
|
62
|
+
# point = "SD28687846".to_wgs84
|
63
|
+
#
|
64
|
+
def transform_to(target_datum)
|
65
|
+
return self if datum == target_datum
|
66
|
+
if helmert = Osgb::Helmert[:"#{self.datum}_to_#{target_datum}"]
|
67
|
+
cartesian_coordinates = Osgb::Ellipsoid[self.datum].polar_to_cartesian(@lat.to_radians,@lng.to_radians)
|
68
|
+
transformed = helmert.transform(*cartesian_coordinates)
|
69
|
+
phi, lambda = Osgb::Ellipsoid[target_datum].cartesian_to_polar(*transformed)
|
70
|
+
self.class.new(phi.to_degrees, lambda.to_degrees, target_datum, precision)
|
71
|
+
else
|
72
|
+
raise Osgb::TransformationError, "Missing helmert transformation for #{self.datum} to #{target_datum}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Tests the equivalence of two points, however they are specified.
|
77
|
+
# If given two Point objects, they can lie on different datums:
|
78
|
+
#
|
79
|
+
# Osgb::Point.new(54.196763, -3.093320, :osgb36) == Osgb::Point.new(54.196915, -3.094684, :wgs84) # -> true
|
80
|
+
#
|
81
|
+
# When comparing a point with a string or array representation they are assumed
|
82
|
+
# to lie on the same datum:
|
83
|
+
#
|
84
|
+
# Osgb::Point.new(54.196763, -3.093320) == "54.196763, -3.093320" # -> true
|
85
|
+
# Osgb::Point.new(54.196763, -3.093320) == [54.196763, -3.093320] # -> true
|
86
|
+
# Osgb::Point.new(54.196763, -3.093320) == "SD28687846" # -> true
|
87
|
+
#
|
88
|
+
# Two points with different precisions will not be considered equivalent.
|
89
|
+
#
|
90
|
+
def ==(other)
|
91
|
+
case other
|
92
|
+
when Osgb::Point
|
93
|
+
other = other.transform_to(self.datum) unless other.datum == self.datum
|
94
|
+
self.lat == other.lat && self.lng == other.lng && self.datum == other.datum
|
95
|
+
when Array
|
96
|
+
self.to_a == other
|
97
|
+
when String
|
98
|
+
self == other.to_latlng(:datum => self.datum) # serves to normalise string representation
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def round(value) #:nodoc:
|
105
|
+
if value.method(:round).arity == 0
|
106
|
+
multiplier = 10**precision
|
107
|
+
(value * multiplier).round.to_f / multiplier
|
108
|
+
else
|
109
|
+
value.round(precision)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
@@ -1,41 +1,135 @@
|
|
1
1
|
class String
|
2
|
+
# Defines a few methods to make the grid reference machinery easily
|
3
|
+
# accessible from string objects.
|
4
|
+
|
5
|
+
# Returns true if the string is a valid grid reference: that is, it consits
|
6
|
+
# of two valid square-designation letters then 4, 6, 8 or 10 digits.
|
7
|
+
# Invalid, malformed and just plain not a grid reference will all return
|
8
|
+
# false.
|
9
|
+
#
|
2
10
|
def is_gridref?
|
3
11
|
!!(self.upcase =~ /^(H(P|T|U|Y|Z)|N(A|B|C|D|F|G|H|J|K|L|M|N|O|R|S|T|U|W|X|Y|Z)|OV|S(C|D|E|G|H|J|K|M|N|O|P|R|S|T|U|W|X|Y|Z)|T(A|F|G|L|M|Q|R|V)){1}\d{4}(NE|NW|SE|SW)?$|((H(P|T|U|Y|Z)|N(A|B|C|D|F|G|H|J|K|L|M|N|O|R|S|T|U|W|X|Y|Z)|OV|S(C|D|E|G|H|J|K|M|N|O|P|R|S|T|U|W|X|Y|Z)|T(A|F|G|L|M|Q|R|V)){1}(\d{4}|\d{6}|\d{8}|\d{10}))$/)
|
4
12
|
end
|
5
13
|
|
14
|
+
# Returns true if the string looks like a grid reference, whether or
|
15
|
+
# not it is well-formed or valid on the ground. In validation this may allow
|
16
|
+
# you to distinguish between mistaken and irrelevant input.
|
17
|
+
#
|
18
|
+
# "HD123456".resembles_gridref? # -> true
|
19
|
+
# "HD123456".is_gridref? # -> false
|
20
|
+
# "SD12345".resembles_gridref? # -> true
|
21
|
+
# "SD12345".is_gridref? # -> false
|
22
|
+
# "WC1 1AA".resembles_gridref? # -> false
|
23
|
+
#
|
24
|
+
def resembles_gridref?
|
25
|
+
!!(self.upcase =~ /^\w\w\d{2,}/)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns true if the string can be decomposed into a valid lat/long
|
29
|
+
# co-ordinate pair.
|
30
|
+
#
|
6
31
|
def is_latlng?
|
32
|
+
coordinates && coordinates.valid?
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns true if the string can be decomposed into a co-ordinate pair,
|
36
|
+
# regardless of whether the coordinates are valid.
|
37
|
+
#
|
38
|
+
def resembles_latlng?
|
7
39
|
!!coordinates
|
8
40
|
end
|
9
41
|
|
10
|
-
|
11
|
-
|
12
|
-
|
42
|
+
# Treats the string as a coordinate pair, if that can be done. Any two
|
43
|
+
# decimal numbers, positive or negative, separated by any non-digit (and
|
44
|
+
# non -) characters are acceptable.
|
45
|
+
#
|
46
|
+
# "54.196763, -3.093320".coordinates # -> [54.196763, -3.093320]
|
47
|
+
#
|
48
|
+
def coordinates(datum=:osgb36, options={})
|
49
|
+
if matches = self.match(/(-?\d+\.\d+)[^\d\-]+(-?\d+\.\d+)/)
|
50
|
+
lat,lng = matches[1,2]
|
51
|
+
Osgb::Point.new(lat, lng, datum, options[:precision])
|
13
52
|
else
|
14
53
|
nil
|
15
54
|
end
|
16
55
|
end
|
17
|
-
|
56
|
+
|
57
|
+
# If the string is a valid grid reference, this returns the lat/long point
|
58
|
+
# using the specified or default datum. Default is WGS84 for GPS compatibility.
|
59
|
+
#
|
18
60
|
def to_latlng(options={})
|
19
61
|
if is_gridref?
|
20
|
-
Osgb::Gridref.new(self, options).to_latlng
|
62
|
+
Osgb::Gridref.new(self, options).to_latlng(options[:datum])
|
21
63
|
else
|
22
|
-
self.coordinates
|
64
|
+
self.coordinates(options[:datum])
|
23
65
|
end
|
24
66
|
end
|
25
67
|
|
68
|
+
# Returns the grid reference as a lat/long pair on the specified or default datum,
|
69
|
+
# raising an exception if it is not valid.
|
70
|
+
#
|
71
|
+
def to_latlng!(options={})
|
72
|
+
with_validity_check { to_latlng(options) }
|
73
|
+
end
|
74
|
+
|
75
|
+
# If the string is a valid grid reference, this returns the coordinate pair
|
76
|
+
# using the OSGB36 datum, which is the native representation for grid references.
|
77
|
+
#
|
78
|
+
def to_osgb36(options={})
|
79
|
+
if is_gridref?
|
80
|
+
Osgb::Gridref.new(self, options).to_latlng(:osgb36)
|
81
|
+
else
|
82
|
+
self.coordinates(:osgb36, options)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the grid reference as a lat/long pair on OSGB36, raising an exception
|
87
|
+
# if it is not valid.
|
88
|
+
#
|
89
|
+
def to_osgb36!(options={})
|
90
|
+
with_validity_check { to_osgb36(options) }
|
91
|
+
end
|
92
|
+
|
93
|
+
# If the string is a valid grid reference, this returns the coordinate pair
|
94
|
+
# using the WGS84 datum, which is the most suitable representation for work
|
95
|
+
# with GPS or google maps.
|
96
|
+
#
|
26
97
|
def to_wgs84(options={})
|
27
98
|
if is_gridref?
|
28
|
-
Osgb::Gridref.new(self, options.
|
99
|
+
Osgb::Gridref.new(self, options).to_latlng(:wgs84)
|
29
100
|
else
|
30
|
-
self.coordinates
|
101
|
+
self.coordinates(:wgs84, options)
|
31
102
|
end
|
32
103
|
end
|
33
104
|
|
105
|
+
# Returns the grid reference as a lat/long pair on WGS84, raising an exception
|
106
|
+
# if it is not valid.
|
107
|
+
#
|
108
|
+
def to_wgs84!(options={})
|
109
|
+
with_validity_check { to_wgs84(options) }
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns the latitude component of the string, however it can be found. Works
|
113
|
+
# for both coordinate pairs and grid references. Defaults to WGS84 if coming from
|
114
|
+
# a grid reference.
|
115
|
+
#
|
34
116
|
def lat(options={})
|
35
|
-
to_latlng(
|
117
|
+
to_latlng(options).lat
|
36
118
|
end
|
37
119
|
|
120
|
+
# Returns the longitude component of the string, however it can be found. Works
|
121
|
+
# for both coordinate pairs and grid references. Defaults to WGS84 if coming from
|
122
|
+
# a grid reference.
|
123
|
+
#
|
38
124
|
def lng(options={})
|
39
|
-
to_latlng(
|
40
|
-
end
|
125
|
+
to_latlng(options).lng
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
|
130
|
+
def with_validity_check(&block)
|
131
|
+
raise Osgb::TransformationError unless is_gridref?
|
132
|
+
block.call
|
133
|
+
end
|
134
|
+
|
41
135
|
end
|
data/spec/point_spec.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Osgb::Point do
|
4
|
+
|
5
|
+
let(:osgb) { Osgb::Point.new(54.196763, -3.093320, :osgb36) }
|
6
|
+
let(:wgs) { Osgb::Point.new(54.196915, -3.094684, :wgs84) }
|
7
|
+
|
8
|
+
it "should have lat and long" do
|
9
|
+
wgs.lat.should == 54.196915
|
10
|
+
wgs.lng.should == -3.094684
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be good at equality" do
|
14
|
+
(wgs == [54.196915, -3.094684]).should be_true
|
15
|
+
(wgs == "54.196915,-3.094684").should be_true
|
16
|
+
(wgs == "54.196915, -3.094684").should be_true
|
17
|
+
(wgs == "SD28687846").should be_true
|
18
|
+
(wgs == "SD13241324").should be_false
|
19
|
+
(wgs == osgb).should be_true
|
20
|
+
(wgs == Osgb::Point.new(54.196914, -3.094684)).should be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be good at helmert transformations" do
|
24
|
+
osgb.transform_to(:wgs84).should == wgs
|
25
|
+
end
|
26
|
+
end
|
data/spec/string_spec.rb
CHANGED
@@ -2,26 +2,54 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe String do
|
4
4
|
it "should know whether or not it looks like a grid reference" do
|
5
|
-
"
|
5
|
+
"SD28687846".resembles_gridref?.should be_true
|
6
|
+
"SD1324132".resembles_gridref?.should be_true
|
7
|
+
"catapult".resembles_gridref?.should be_false
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should know whether or not it is a grid reference" do
|
11
|
+
"SD28687846".is_gridref?.should be_true
|
12
|
+
"SD1324132".is_gridref?.should be_false
|
6
13
|
"catapult".is_gridref?.should be_false
|
7
14
|
"54.196915 -3.094684".is_gridref?.should be_false
|
8
15
|
end
|
9
16
|
|
10
17
|
it "should know whether or not it looks like a lat/lng pair" do
|
11
|
-
"
|
18
|
+
"SD28687846".resembles_latlng?.should be_false
|
19
|
+
"catapult".resembles_latlng?.should be_false
|
20
|
+
"54.196915 -3.094684".resembles_latlng?.should be_true
|
21
|
+
"54.196915, -3.094684".resembles_latlng?.should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should know whether or not it is a lat/lng pair" do
|
25
|
+
"SD28687846".is_latlng?.should be_false
|
12
26
|
"catapult".is_latlng?.should be_false
|
13
27
|
"54.196915 -3.094684".is_latlng?.should be_true
|
28
|
+
"104.196915 -3.094684".is_latlng?.should be_false
|
29
|
+
"54.196915 -183.094684".is_latlng?.should be_false
|
14
30
|
"54.196915, -3.094684".is_latlng?.should be_true
|
15
31
|
"54.196915|-3.094684".is_latlng?.should be_true
|
32
|
+
"54.196915:-3.094684".is_latlng?.should be_true
|
33
|
+
"54.196915x-3.094684".is_latlng?.should be_true
|
16
34
|
end
|
17
35
|
|
18
36
|
it "should turn be able to turn itself into an osgb lat/lng pair" do
|
19
|
-
"SD28687846".
|
20
|
-
"SD28687846".
|
37
|
+
"SD28687846".to_osgb36(:precision => 6).to_a.should == [54.196763, -3.093320]
|
38
|
+
"SD28687846".to_osgb36(:precision => 6).to_s.should == "54.196763,-3.09332"
|
39
|
+
"SD28687846".to_osgb36(:precision => 2).to_a.should == [54.2, -3.09]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should turn be able to turn itself into a wgs84 lat/lng pair" do
|
43
|
+
"SD28687846".to_wgs84(:precision => 6).to_a.should == [54.196915, -3.094684]
|
44
|
+
"SD28687846".to_wgs84(:precision => 2).to_a.should == [54.2, -3.09]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should default to WGS84 representation" do
|
48
|
+
"SD28687846".to_latlng(:precision => 6).to_a.should == [54.196915, -3.094684]
|
21
49
|
end
|
22
50
|
|
23
|
-
it "should
|
24
|
-
"SD28687846".
|
25
|
-
"
|
51
|
+
it "should blow up if banged" do
|
52
|
+
lambda { "SD28687846".to_latlng!(:precision => 6) }.should_not raise_error
|
53
|
+
lambda { "SD2868784".to_latlng!(:precision => 6) }.should raise_error(Osgb::TransformationError)
|
26
54
|
end
|
27
55
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: osgb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- William Ross
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-25 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rspec
|
@@ -63,17 +63,19 @@ files:
|
|
63
63
|
- lib/osgb/gridref.rb
|
64
64
|
- lib/osgb/has_gridref.rb
|
65
65
|
- lib/osgb/helmert.rb
|
66
|
+
- lib/osgb/point.rb
|
66
67
|
- lib/osgb/projection.rb
|
67
68
|
- lib/osgb/railtie.rb
|
68
69
|
- lib/osgb/string_conversions.rb
|
69
70
|
- lib/osgb.rb
|
71
|
+
- spec/point_spec.rb
|
70
72
|
- spec/spec_helper.rb
|
71
73
|
- spec/string_spec.rb
|
72
74
|
- CHANGELOG.rdoc
|
73
75
|
- Gemfile
|
74
76
|
- LICENSE
|
75
77
|
- Rakefile
|
76
|
-
- README.
|
78
|
+
- README.markdown
|
77
79
|
- init.rb
|
78
80
|
homepage: http://github.com/spanner/osgb
|
79
81
|
licenses: []
|
data/README.md
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
= OSGB
|
2
|
-
|
3
|
-
Wiki[https://github.com/spanner/osgb/wiki] RDocs[http://rdoc.info/projects/spanner/osgb]
|
4
|
-
|
5
|
-
Osgb is a library that converts between British (and Irish) grid references and latitude and longitude co-ordinates. It is precise to about 5m, which is to say it's good enough for WGS84 and most GPS use but not local enough for surveying to ETRS89.
|
6
|
-
|
7
|
-
== Installation
|
8
|
-
|
9
|
-
In <b>Rails 3</b>, add this to your Gemfile and run +bundle install+.
|
10
|
-
|
11
|
-
gem "osgb"
|
12
|
-
|
13
|
-
In <b>Rails 2</b>, add this to your environment.rb file.
|
14
|
-
|
15
|
-
config.gem "osgb"
|
16
|
-
|
17
|
-
Alternatively, you can install it as a plugin.
|
18
|
-
|
19
|
-
rails plugin install git://github.com/spanner/osgb.git
|
20
|
-
|
21
|
-
== Usage
|
22
|
-
|
23
|
-
You don't need to make any explicit reference to the gem. It adds conversion methods to the String class:
|
24
|
-
|
25
|
-
"SD12341234".is_gridref? # -> true
|
26
|
-
"SD12341234".to_latlng # ->
|
27
|
-
"SD12341234".to_WGS84 # ->
|
28
|
-
"1.056789, 55.98978607".is_latlng? # -> true
|
29
|
-
"1.056789, 55.98978607".to_gridref # -> true
|
30
|
-
|
31
|
-
and provides some help for your ActiveRecord classes:
|
32
|
-
|
33
|
-
class Checkpoint < ActiveRecord::Base
|
34
|
-
has_gridref :lat => 'lat',
|
35
|
-
:lng => 'lng',
|
36
|
-
:gridref => 'gridref',
|
37
|
-
:validation => false,
|
38
|
-
:converstion => true
|
39
|
-
|
40
|
-
The :lat, :lng and :gridref keys should pass in the names of the relevant columns if they don't match these defaults.
|
41
|
-
|
42
|
-
== Questions or Problems?
|
43
|
-
|
44
|
-
If you have any issues with CanCan which you cannot find the solution to in the documentation[https://github.com/ryanb/cancan/wiki], please add an {issue on GitHub}[https://github.com/ryanb/cancan/issues] or fork the project and send a pull request.
|
45
|
-
|
46
|
-
To get the specs running you should call +bundle+ and then +rake+. See the {spec/README}[https://github.com/ryanb/cancan/blob/master/spec/README.rdoc] for more information.
|
47
|
-
|
48
|
-
|
49
|
-
== Special Thanks
|
50
|
-
|
51
|
-
CanCan was inspired by declarative_authorization[https://github.com/stffn/declarative_authorization/] and aegis[https://github.com/makandra/aegis]. Also many thanks to the CanCan contributors[https://github.com/ryanb/cancan/contributors]. See the CHANGELOG[https://github.com/ryanb/cancan/blob/master/CHANGELOG.rdoc] for the full list.
|