SVY21 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/svy21.rb +201 -0
- metadata +49 -0
data/lib/svy21.rb
ADDED
@@ -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:
|