SVY21 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/svy21.rb +201 -0
  2. metadata +49 -0
@@ -0,0 +1,201 @@
1
+ # The SVY21 class provides functionality to convert between the SVY21 and Lat/Lon coordinate systems.
2
+ #
3
+ # Internally, the class uses the equations specified in the following web page to perform the conversion.
4
+ # http://www.linz.govt.nz/geodetic/conversion-coordinates/projection-conversions/transverse-mercator-preliminary-computations/index.aspx
5
+ class SVY21
6
+ # Ratio to convert degrees to radians.
7
+ RAD_RATIO = Math::PI / 180.0
8
+
9
+ # Datum and Projection.
10
+
11
+ # Semi-major axis of reference ellipsoid.
12
+ A = 6378137.0
13
+ # Ellipsoidal flattening
14
+ F = 1.0 / 298.257223563
15
+ # Origin latitude (degrees).
16
+ ORIGIN_LATITUDE = 1.366666
17
+ # Origin longitude (degrees).
18
+ ORIGIN_LONGITUDE = 103.833333
19
+ # False Northing.
20
+ FALSE_NORTHING = 38744.572
21
+ # False Easting.
22
+ FALSE_EASTING = 28001.642
23
+ # Central meridian scale factor.
24
+ K = 1.0
25
+
26
+ # Computed Projection Constants
27
+
28
+ # Naming convention: the trailing number is the power of the variable.
29
+
30
+ # Semi-minor axis of reference ellipsoid.
31
+ B = A * (1.0 - F)
32
+ # Squared eccentricity of reference ellipsoid.
33
+ E2 = (2.0 * F) - (F * F)
34
+ E4 = E2 * E2
35
+ E6 = E4 * E2
36
+
37
+ # Naming convention: A0..6 are terms in an expression, not powers.
38
+
39
+ A0 = 1.0 - (E2 / 4.0) - (3.0 * E4 / 64.0) - (5.0 * E6 / 256.0)
40
+ A2 = (3.0 / 8.0) * (E2 + (E4 / 4.0) + (15.0 * E6 / 128.0))
41
+ A4 = (15.0 / 256.0) * (E4 + (3.0 * E6 / 4.0))
42
+ A6 = 35.0 * E6 / 3072.0
43
+
44
+ # Naming convention: the trailing number is the power of the variable.
45
+
46
+ N = (A - B) / (A + B)
47
+ N2 = N * N
48
+ N3 = N2 * N
49
+ N4 = N2 * N2
50
+
51
+ G = A * (1.0 - N) * (1.0 - N2) * (1.0 + (9.0 * N2 / 4.0) + (225.0 * N4 / 64.0)) * RAD_RATIO
52
+
53
+ # M: meridian distance.
54
+ def self.calc_m(lat)
55
+ lat_r = lat * RAD_RATIO
56
+
57
+ A * ((A0 * lat_r) - (A2 * Math.sin(2.0 * lat_r)) + (A4 * Math.sin(4.0 * lat_r)) - (A6 * Math.sin(6.0 * lat_r)))
58
+ end
59
+
60
+ # Rho: radius of curvature of meridian.
61
+ def self.calc_rho(sin_2_lat)
62
+ num = A * (1.0 - E2)
63
+ denom = (1.0 - E2 * sin_2_lat) ** (3.0 / 2.0)
64
+
65
+ num / denom
66
+ end
67
+
68
+ # v: radius of curvature in the prime vertical.
69
+ def self.calc_v(sin_2_lat)
70
+ poly = 1.0 - E2 * sin_2_lat
71
+
72
+ A / Math.sqrt(poly)
73
+ end
74
+
75
+ # Computes Latitude and Longitude based on an SVY21 coordinate.
76
+ #
77
+ # This method returns an array object that contains two numbers,
78
+ # latitude, accessible with [0], and
79
+ # longitude, accessible with [1].
80
+ #
81
+ # @param northing Northing based on SVY21.
82
+ # @param easting Easting based on SVY21.
83
+ #
84
+ # @return the conversion result as an array, [lat, lon].
85
+ def self.svy21_to_lat_lon(northing, easting)
86
+ n_prime = northing - FALSE_NORTHING
87
+ m_origin = calc_m(ORIGIN_LATITUDE)
88
+ m_prime = m_origin + (n_prime / K)
89
+ sigma = (m_prime / G) * RAD_RATIO
90
+
91
+ # Naming convention: lat_prime_t1..4 are terms in an expression, not powers.
92
+ lat_prime_t1 = ((3.0 * N / 2.0) - (27.0 * N3 / 32.0)) * Math.sin(2.0 * sigma)
93
+ lat_prime_t2 = ((21.0 * N2 / 16.0) - (55.0 * N4 / 32.0)) * Math.sin(4.0 * sigma)
94
+ lat_prime_t3 = (151.0 * N3 / 96.0) * Math.sin(6.0 * sigma)
95
+ lat_prime_t4 = (1097.0 * N4 / 512.0) * Math.sin(8.0 * sigma)
96
+ lat_prime = sigma + lat_prime_t1 + lat_prime_t2 + lat_prime_t3 + lat_prime_t4
97
+
98
+ # Naming convention: sin_2_lat_prime = "square of sin(lat_prime)" = sin(lat_prime) ** 2.0
99
+ sin_lat_prime = Math.sin(lat_prime)
100
+ sin_2_lat_prime = sin_lat_prime * sin_lat_prime
101
+
102
+ # Naming convention: the trailing number is the power of the variable.
103
+ rho_prime = calc_rho(sin_2_lat_prime)
104
+ v_prime = calc_v(sin_2_lat_prime)
105
+ psi_prime = v_prime / rho_prime
106
+ psi_prime_2 = psi_prime * psi_prime
107
+ psi_prime_3 = psi_prime_2 * psi_prime
108
+ psi_prime_4 = psi_prime_3 * psi_prime
109
+ t_prime = Math.tan(lat_prime)
110
+ t_prime_2 = t_prime * t_prime
111
+ t_prime_4 = t_prime_2 * t_prime_2
112
+ t_prime_6 = t_prime_4 * t_prime_2
113
+ e_prime = easting - FALSE_EASTING
114
+ x = e_prime / (K * v_prime)
115
+ x2 = x * x
116
+ x3 = x2 * x
117
+ x5 = x3 * x2
118
+ x7 = x5 * x2
119
+
120
+ # Compute Latitude
121
+ # Naming convention: lat_term_1..4 are terms in an expression, not powers.
122
+ lat_factor = t_prime / (K * rho_prime)
123
+ lat_term_1 = lat_factor * ((e_prime * x) / 2.0)
124
+ lat_term_2 = lat_factor * ((e_prime * x3) / 24.0) * ((-4.0 * psi_prime_2 + (9.0 * psi_prime) * (1.0 - t_prime_2) + (12.0 * t_prime_2)))
125
+ lat_term_3 = lat_factor * ((e_prime * x5) / 720.0) * ((8.0 * psi_prime_4) * (11.0 - 24.0 * t_prime_2) - (12.0 * psi_prime_3) * (21.0 - 71.0 * t_prime_2) + (15.0 * psi_prime_2) * (15.0 - 98.0 * t_prime_2 + 15.0 * t_prime_4) + (180.0 * psi_prime) * (5.0 * t_prime_2 - 3.0 * t_prime_4) + 360.0 * t_prime_4)
126
+ lat_term_4 = lat_factor * ((e_prime * x7) / 40320.0) * (1385.0 - 3633.0 * t_prime_2 + 4095.0 * t_prime_4 + 1575.0 * t_prime_6)
127
+ lat = lat_prime - lat_term_1 + lat_term_2 - lat_term_3 + lat_term_4
128
+
129
+ # Compute Longitude
130
+ # Naming convention: lon_term_1..4 are terms in an expression, not powers.
131
+ sec_lat_prime = 1.0 / Math.cos(lat)
132
+ lon_term_1 = x * sec_lat_prime
133
+ lon_term_2 = ((x3 * sec_lat_prime) / 6.0) * (psi_prime + 2.0 * t_prime_2)
134
+ lon_term_3 = ((x5 * sec_lat_prime) / 120.0) * ((-4.0 * psi_prime_3) * (1.0 - 6.0 * t_prime_2) + psi_prime_2 * (9.0 - 68.0 * t_prime_2) + 72.0 * psi_prime * t_prime_2 + 24.0 * t_prime_4)
135
+ lon_term_4 = ((x7 * sec_lat_prime) / 5040.0) * (61.0 + 662.0 * t_prime_2 + 1320.0 * t_prime_4 + 720.0 * t_prime_6)
136
+ lon = (ORIGIN_LONGITUDE * RAD_RATIO) + lon_term_1 - lon_term_2 + lon_term_3 - lon_term_4
137
+
138
+ [lat / RAD_RATIO, lon / RAD_RATIO]
139
+ end
140
+
141
+ # Computes SVY21 Northing and Easting based on a Latitude and Longitude coordinate.
142
+ #
143
+ # This method returns an array object that contains two numbers,
144
+ # northing, accessible with [0], and
145
+ # easting, accessible with [1].
146
+ #
147
+ # @param latitude latitude in degrees.
148
+ # @param longitude longitude in degrees.
149
+ #
150
+ # @return the conversion result as an array, [northing, easting].
151
+ def self.lat_lon_to_svy21(latitude, longitude)
152
+ # Naming convention: sin_2_lat = "square of sin(lat)" = sin(lat) ** 2.0
153
+ lat_r = latitude * RAD_RATIO
154
+ sin_lat = Math.sin(lat_r)
155
+ sin_2_lat = sin_lat * sin_lat
156
+ cos_lat = Math.cos(lat_r)
157
+ cos_2_lat = cos_lat * cos_lat
158
+ cos_3_lat = cos_2_lat * cos_lat
159
+ cos_4_lat = cos_3_lat * cos_lat
160
+ cos_5_lat = cos_3_lat * cos_2_lat
161
+ cos_6_lat = cos_5_lat * cos_lat
162
+ cos_7_lat = cos_5_lat * cos_2_lat
163
+
164
+ rho = calc_rho(sin_2_lat)
165
+ v = calc_v(sin_2_lat)
166
+ psi = v / rho
167
+ t = Math.tan(lat_r)
168
+ w = (longitude - ORIGIN_LONGITUDE) * RAD_RATIO
169
+ m = calc_m(latitude)
170
+ origin_m = calc_m(ORIGIN_LATITUDE)
171
+
172
+ # Naming convention: the trailing number is the power of the variable.
173
+ w2 = w * w
174
+ w4 = w2 * w2
175
+ w6 = w4 * w2
176
+ w8 = w6 * w2
177
+ psi2 = psi * psi
178
+ psi3 = psi2 * psi
179
+ psi4 = psi2 * psi2
180
+ t2 = t * t
181
+ t4 = t2 * t2
182
+ t6 = t4 * t2
183
+
184
+ # Compute Northing.
185
+ # Naming convention: n_term_1..4 are terms in an expression, not powers.
186
+ n_term_1 = w2 / 2.0 * v * sin_lat * cos_lat
187
+ n_term_2 = w4 / 24.0 * v * sin_lat * cos_3_lat * (4.0 * psi2 + psi - t2)
188
+ n_term_3 = w6 / 720.0 * v * sin_lat * cos_5_lat * ((8.0 * psi4) * (11.0 - 24.0 * t2) - (28.0 * psi3) * (1.0 - 6.0 * t2) + psi2 * (1.0 - 32.0 * t2) - psi * 2.0 * t2 + t4)
189
+ n_term_4 = w8 / 40320.0 * v * sin_lat * cos_7_lat * (1385.0 - 3111.0 * t2 + 543.0 * t4 - t6)
190
+ northing = FALSE_NORTHING + K * (m - origin_m + n_term_1 + n_term_2 + n_term_3 + n_term_4)
191
+
192
+ # Compute Easting.
193
+ # Naming convention: e_term_1..3 are terms in an expression, not powers.
194
+ e_term_1 = w2 / 6.0 * cos_2_lat * (psi - t2)
195
+ e_term_2 = w4 / 120.0 * cos_4_lat * ((4.0 * psi3) * (1.0 - 6.0 * t2) + psi2 * (1.0 + 8.0 * t2) - psi * 2.0 * t2 + t4)
196
+ e_term_3 = w6 / 5040.0 * cos_6_lat * (61.0 - 479.0 * t2 + 179.0 * t4 - t6)
197
+ easting = FALSE_EASTING + K * v * w * cos_lat * (1 + e_term_1 + e_term_2 + e_term_3)
198
+
199
+ [northing, easting]
200
+ end
201
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: SVY21
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Camillus Gerard Cai
9
+ - Stan Chang Khin Boon
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-04-07 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description:
16
+ email:
17
+ - cgcai@qxcg.net
18
+ - khinboon@gmail.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - lib/svy21.rb
24
+ homepage: https://github.com/cgcai/SVY21
25
+ licenses: []
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 1.8.24
45
+ signing_key:
46
+ specification_version: 3
47
+ summary: A library to convert between Lat/Lon, and SVY21.
48
+ test_files: []
49
+ has_rdoc: