SVY21 1.0.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.
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: