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