osgb 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|