quad_sphere 0.9.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.
@@ -0,0 +1,236 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'quad_sphere'
4
+ require 'quad_sphere/tangential'
5
+
6
+ module QuadSphere
7
+
8
+ # Implements the quadrilateralised spherical cube projection.
9
+ #
10
+ # The quadrilateralised spherical cube projection, or "Quad Sphere"
11
+ # or "COBE Sky Cube" (CSC), applies a further curvilinear
12
+ # transformation to the tangential spherical cube projection, to
13
+ # make it approximately equal-area. This is useful for storing
14
+ # spherical data in a raster: then you can integrate directly on the
15
+ # cube face planes, without having to map your data back to the
16
+ # original sphere.
17
+ #
18
+ # This projection has been used extensively by the Cosmic Background
19
+ # Explorer project (COBE), and this implementation _should_ match
20
+ # results from their software. This can't be guaranteed, though, as
21
+ # this author hasn't pursued finding and testing raw COBE data.
22
+ # There's a to-do, right there.
23
+ #
24
+ # Mind that this implements only the spherical projection, not the
25
+ # scheme for storing pixels along a Z-order curve, that is described
26
+ # in length as part of the COBE data format. We don't deal with
27
+ # pixels at all.
28
+ #
29
+ # Also: this implements the projection described by Chan & O'Neill,
30
+ # 1975, _not_ the one by Laubscher & O'Neill, 1976, which is
31
+ # non-differentiable along the diagonals.
32
+ #
33
+ # As Chan's original report is not readily available, this
34
+ # implementation is based on formulae found in FITS WCS documents.
35
+ # Specifically: "Representations of celestial coordinates in FITS
36
+ # (Paper II)", Calabretta, M. R., and Greisen, E. W., <i>Astronomy &
37
+ # Astrophysics</i>, 395, 1077-1122, 2002. This is available from
38
+ # the FITS Support Office at NASA/GSFC; see reference below. And
39
+ # mind the possible implications of this on the accuracy of the
40
+ # transformation, discussed in the documentation of
41
+ # {forward_distort}.
42
+ #
43
+ # @see http://lambda.gsfc.nasa.gov/product/cobe/skymap_info_new.cfm
44
+ # COBE Quadrilateralized Spherical Cube at NASA's data center for
45
+ # Cosmic Microwave Background research.
46
+ # @see http://fits.gsfc.nasa.gov/fits_wcs.html
47
+ # FITS World Coordinate System Documents at NASA/GSFC.
48
+ # @see http://en.wikipedia.org/wiki/Quadrilateralized_spherical_cube
49
+ # Quadrilateralized spherical cube at Wikipedia.
50
+ # @see http://gis.stackexchange.com/questions/40957/is-the-quadrilateralized-spherical-cube-map-projection-the-same-as-snyders-cubi
51
+ # Remarks by (allegedly) Kenneth Chan regarding his and
52
+ # Laubscher's work, at the GIS StackExchange.
53
+ # @see http://www.progonos.com/furuti/MapProj/Normal/ProjPoly/projPoly2.html
54
+ # Pretty maps at Carlos A. Furuti's website.
55
+ #
56
+ # @author César Rincón
57
+ module CSC
58
+
59
+ # Computes the projection of a point on the surface of the sphere,
60
+ # given in spherical coordinates (φ,θ), to a point of cartesian
61
+ # coordinates (x,y) on one of the six cube faces.
62
+ #
63
+ # @param phi (Float) the φ angle in radians, from -π to π (or 0 to
64
+ # to 2π, if you like). This is the azimuth, or longitude
65
+ # (spherical, not geodetic).
66
+ # @param theta (Float) the θ angle in radians, from -π/2 to π/2.
67
+ # This is the elevation, or latitude (spherical, not geodetic).
68
+ #
69
+ # @return (Array) an array of three elements: the identifier of
70
+ # the face (see constants in {QuadSphere}), the _x_ coordinate
71
+ # of the projected point, and the _y_ coordinate of the
72
+ # projected point. Both coordinates will be in the range -1 to
73
+ # 1.
74
+ #
75
+ # @see inverse
76
+ def self.forward(phi, theta)
77
+ face, chi, psi = Tangential.forward(phi, theta)
78
+ [ face, forward_distort(chi,psi), forward_distort(psi,chi) ]
79
+ end
80
+
81
+ # Computes the projection of a point at cartesian coordinates
82
+ # (x,y) on one of the six cube faces, to a point at spherical
83
+ # coordinates (φ,θ) on the surface of the sphere.
84
+ #
85
+ # Note that, while the projection is reversible for points within
86
+ # each of the cube faces, it is not necessarily so for points
87
+ # located exactly on the edges. This is because points on the
88
+ # edges of the cube are shared amongst two or even three faces,
89
+ # and the forward projection may return an alternative mapping to
90
+ # a neighbouring face.
91
+ #
92
+ # @param face (Integer) the identifier of the cube face; see
93
+ # constants in {QuadSphere}.
94
+ # @param x (Float) the _x_ coordinate of the point within the
95
+ # face, from -1.0 to 1.0.
96
+ # @param y (Float) the _y_ coordinate of the point within the
97
+ # face, from -1.0 to 1.0.
98
+ #
99
+ # @return (Array) an array of two elements: the φ angle in radians
100
+ # (azimuth or longitude - spherical, not geodetic), from -π to
101
+ # π; and the θ angle in radians, from -π/2 to π/2 (elevation or
102
+ # latitude - spherical, not geodetic).
103
+ #
104
+ # @see forward
105
+ def self.inverse(face, x, y)
106
+ chi = inverse_distort(x,y)
107
+ psi = inverse_distort(y,x)
108
+ Tangential.inverse(face, chi, psi)
109
+ end
110
+
111
+ # This performs the forward curvilinear transformation of
112
+ # coordinates on a cube face, from the basic tangential to the CSC
113
+ # projection. Specifically, the function computes the adjusted
114
+ # cartesian coordinate _x_ from the original coordinates χ,ψ
115
+ # produced by tangential projection. To compute the _y_
116
+ # coordinate, evaluate again for ψ,χ.
117
+ #
118
+ # This is not an exact transformation, and its accuracy is further
119
+ # impaired by the fact that the math foundation behind the CSC
120
+ # projection is not publicly available - the closest we have to a
121
+ # "gold standard" is the polynomial approximation put forward in
122
+ # the Calabretta paper. Equivalent calculations appear almost
123
+ # verbatim in published COBE Data Analysis Software, available at
124
+ # http://lambda.gsfc.nasa.gov/product/cobe/cgis.cfm
125
+ # (brush up on your FORTRAN and download +cgis-for.tar.gz+).
126
+ #
127
+ # Of interest: a user of the the GIS StackExchange, allegedly
128
+ # Kenneth Chan, the original designer of this projection, had this
129
+ # to say on this particular topic:
130
+ #
131
+ # "Alex, When you get my report, you will notice that my direct
132
+ # and inverse mappings are accurate to 4 or 5 significant
133
+ # figures. Laubscher changed my coefficients in his report. A
134
+ # comparison reveals that the transformations given in
135
+ # Calabretta's paper do not bear any resemblance to mine. The
136
+ # coefficients are not from my original report but are based on
137
+ # Laubscher's report. Because of this, Calabretta states that
138
+ # my transformations are accurate to 1%. Moreover, it is also
139
+ # stated that the code disagrees with the formulas. Please bear
140
+ # this in mind. My advice is to go by the report in the original
141
+ # for [...]"
142
+ #
143
+ # (link to the thread in the module doc above)
144
+ #
145
+ # So our coefficients may not be Chan's - ours are indeed the ones
146
+ # in the Calabretta paper. We are trying to contact Ken Chan for
147
+ # clarification... and/or a copy of his 1975 paper, that would be
148
+ # brilliant. On the other hand, this is the exact same
149
+ # computation done by actual COBE software, apparently, so it
150
+ # probably matches their results. Go figure.
151
+ #
152
+ # In our own tests of this implementation, when transforming (χ,ψ)
153
+ # to (x,y) and back to (χ',ψ'), where the domain of all four
154
+ # variables is -1.0 to 1.0, we've found the mean closure error
155
+ # (mean distance from point (χ,ψ) to point (χ',ψ')) to be
156
+ # 4.152E-05, with a standard deviation of 3.72E-05, and a maximum
157
+ # error of 2.33E-04 in small regions around the origin and
158
+ # corners.
159
+ #
160
+ # To put this in perspective: if you were mapping the Earth, then
161
+ # the domain of this transformation is a cube face, then the mean
162
+ # closure error would be around 415 metres, with a standard
163
+ # deviation of 372 m, and small zones of high error near the cube
164
+ # face centres where it can get as bad as 2.3 km (very very quick
165
+ # approximation there, don't quote me on those figures). So aye,
166
+ # we could use more precision.
167
+ #
168
+ # There is code in the +extras+ directory to compute the error
169
+ # distribution, and even generate a picture showing the areas of
170
+ # low and high error on the plane. Peek there if you're curious.
171
+ #
172
+ # @see inverse_distort
173
+ def self.forward_distort(chi, psi)
174
+ chi2 = chi**2
175
+ chi3 = chi**3
176
+ psi2 = psi**2
177
+ omchi2 = 1.0 - chi2
178
+ chi*(1.37484847732 - 0.37484847732*chi2) +
179
+ chi*psi2*omchi2*(-0.13161671474 +
180
+ 0.136486206721*chi2 +
181
+ (1.0 - psi2) *
182
+ (0.141189631152 +
183
+ psi2*(-0.281528535557 + 0.106959469314*psi2) +
184
+ chi2*(0.0809701286525 +
185
+ 0.15384112876*psi2 -
186
+ 0.178251207466*chi2))) +
187
+ chi3*omchi2*(-0.159596235474 -
188
+ (omchi2 * (0.0759196200467 - 0.0217762490699*chi2)))
189
+ end
190
+
191
+ # This performs the inverse curvilinear transformation of
192
+ # coordinates on a cube face, from the CSC to the basic tangential
193
+ # projection. Specifically, the function computes the original
194
+ # cartesian coordinate χ produced by tangential projection, from
195
+ # the adjusted coordinates x,y. To compute the coordinate ψ,
196
+ # evaluate again for y,x.
197
+ #
198
+ # @see forward_distort Notes regarding accuracy in the
199
+ # documentation of forward_distort.
200
+ def self.inverse_distort(x, y)
201
+ # This is the sum Σ, from j=0 to 6, of the sum Σ, from i=0 to 6-j,
202
+ # of Pij * (X**(2*i)) * (Y**(2*j))
203
+
204
+ # We unroll.
205
+ x2 = x*x
206
+ x4 = x**4
207
+ x6 = x**6
208
+ x8 = x**8
209
+ x10 = x**10
210
+ x12 = x**12
211
+ y2 = y*y
212
+ y4 = y**4
213
+ y6 = y**6
214
+ y8 = y**8
215
+ y10 = y**10
216
+ y12 = y**12
217
+
218
+ x + x*(1 - x2) *
219
+ (-0.27292696 - 0.07629969 * x2 -
220
+ 0.22797056 * x4 + 0.54852384 * x6 -
221
+ 0.62930065 * x8 + 0.25795794 * x10 +
222
+ 0.02584375 * x12 - 0.02819452 * y2 -
223
+ 0.01471565 * x2 * y2 + 0.48051509 * x4 * y2 -
224
+ 1.74114454 * x6 * y2 + 1.71547508 * x8 * y2 -
225
+ 0.53022337 * x10 * y2 + 0.27058160 * y4 -
226
+ 0.56800938 * x2 * y4 + 0.30803317 * x4 * y4 +
227
+ 0.98938102 * x6 * y4 - 0.83180469 * x8 * y4 -
228
+ 0.60441560 * y6 + 1.50880086 * x2 * y6 -
229
+ 0.93678576 * x4 * y6 + 0.08693841 * x6 * y6 +
230
+ 0.93412077 * y8 - 1.41601920 * x2 * y8 +
231
+ 0.33887446 * x4 * y8 - 0.63915306 * y10 +
232
+ 0.52032238 * x2 * y10 + 0.14381585 * y12)
233
+ end
234
+
235
+ end # module CSC
236
+ end #module QuadSphere
@@ -0,0 +1,131 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module QuadSphere
4
+
5
+ # Implements the tangential spherical cube projection.
6
+ #
7
+ # In this projection, points on the sphere are projected from the
8
+ # centre of the sphere onto the six faces of an inscribed cube.
9
+ # Thus, the projection consists of six planar faces, each of which
10
+ # is a gnomonic projection of a portion of the sphere.
11
+ #
12
+ # This module exists solely because the CSC projection is a
13
+ # distortion of the mapping produced by a tangential projection.
14
+ # Thus, the forward and inverse computations here are required, but
15
+ # they have been segregated on the oft chance that they could be
16
+ # useful on their own.
17
+ #
18
+ # @see http://en.wikipedia.org/wiki/Gnomonic_projection
19
+ # Gnomonic projection at Wikipedia.
20
+ #
21
+ # @author Cesar Rincon
22
+ module Tangential
23
+
24
+ # Information for each face. Faces are given in the order: top,
25
+ # front, left, back, right, bottom.
26
+ #
27
+ # These procedures rearrange the direction cosines as appropriate
28
+ # for the face we're projecting. The 3 values returned are ξ, η,
29
+ # and ζ.
30
+ FORWARD_PARAMETERS =
31
+ [ Proc.new{ |l, m, n| [ m, -l, n ] },
32
+ Proc.new{ |l, m, n| [ m, n, l ] },
33
+ Proc.new{ |l, m, n| [ -l, n, m ] },
34
+ Proc.new{ |l, m, n| [ -m, n, -l ] },
35
+ Proc.new{ |l, m, n| [ l, n, -m ] },
36
+ Proc.new{ |l, m, n| [ m, l, -n ] } ]
37
+ private_constant :FORWARD_PARAMETERS \
38
+ if self.respond_to?(:private_constant) # don't die for this in 1.8
39
+
40
+ # Computes the projection of a point on the surface of the sphere,
41
+ # given in spherical coordinates (φ,θ), to a point of cartesian
42
+ # coordinates (χ,ψ) on one of the six cube faces.
43
+ #
44
+ # @param phi (Float) the φ angle in radians, from -π to π (or 0 to
45
+ # to 2π, if you like). This is the azimuth, or longitude
46
+ # (spherical, not geodetic).
47
+ # @param theta (Float) the θ angle in radians, from -π/2 to π/2.
48
+ # This is the elevation, or latitude (spherical, not geodetic).
49
+ #
50
+ # @return (Array) an array of three elements: the identifier of
51
+ # the face (see constants in {QuadSphere}), the χ coordinate of
52
+ # the projected point, and the ψ coordinate of the projected
53
+ # point. Both coordinates will be in the range -1 to 1.
54
+ def self.forward(phi, theta)
55
+ # compute the direction cosines
56
+ l = Math::cos(theta) * Math::cos(phi)
57
+ m = Math::cos(theta) * Math::sin(phi)
58
+ n = Math::sin(theta)
59
+
60
+ # identify the face, and adjust our parameters.
61
+ max, face = nil, -1
62
+ [ n, l, m, -l, -m, -n ].each_with_index do |v, i|
63
+ max, face = v, i if max.nil? || v > max
64
+ end
65
+
66
+ xi, eta, zeta = FORWARD_PARAMETERS[face].call(l,m,n)
67
+
68
+ # Compute χ and ψ.
69
+ # XXX - This will blow up if ζ is zero... can it happen?
70
+ chi = xi / zeta
71
+ psi = eta / zeta
72
+
73
+ # Out of curiosity: why does Calabretta do this?
74
+ # x = phi_c + Math::PI/4 * chi
75
+ # y = theta_c + Math::PI/4 * psi
76
+
77
+ [face,chi,psi]
78
+ end
79
+
80
+ # Information for each face. Faces are given in the order:
81
+ # top, front, left, back, right, bottom.
82
+ #
83
+ # These procedures return the direction cosines:
84
+ # 1. l (cos(θ)*cos(φ))
85
+ # 2. m (cos(θ)*sin(φ))
86
+ # 3. n (sin(θ))
87
+ INVERSE_PARAMETERS =
88
+ [ Proc.new{ |xi, eta, zeta| [ -eta, xi, zeta ] },
89
+ Proc.new{ |xi, eta, zeta| [ zeta, xi, eta ] },
90
+ Proc.new{ |xi, eta, zeta| [ -xi, zeta, eta ] },
91
+ Proc.new{ |xi, eta, zeta| [ -zeta, -xi, eta ] },
92
+ Proc.new{ |xi, eta, zeta| [ xi, -zeta, eta ] },
93
+ Proc.new{ |xi, eta, zeta| [ eta, xi, -zeta ] } ]
94
+ private_constant :INVERSE_PARAMETERS \
95
+ if self.respond_to?(:private_constant) # don't die for this in 1.8
96
+
97
+ # Computes the projection of a point at cartesian coordinates
98
+ # (χ,ψ) on one of the six cube faces, to a point at spherical
99
+ # coordinates (φ,θ) on the surface of the sphere.
100
+ #
101
+ # Note that, while the projection is reversible for points within
102
+ # each of the cube faces, it is not necessarily so for points
103
+ # located exactly on the edges. This is because points on the
104
+ # edges of the cube are shared amongst two or even three faces,
105
+ # and the forward projection may return an alternative mapping to
106
+ # a neighbouring face.
107
+ #
108
+ # @param face (Integer) the identifier of the cube face; see
109
+ # constants in {QuadSphere}.
110
+ # @param chi (Float) the χ coordinate of the point within the
111
+ # face, from -1.0 to 1.0.
112
+ # @param psi (Float) the ψ coordinate of the point within the
113
+ # face, from -1.0 to 1.0.
114
+ #
115
+ # @return (Array) an array of two elements: the φ angle in radians
116
+ # (azimuth or longitude - spherical, not geodetic), from -π to
117
+ # π; and the θ angle in radians, from -π/2 to π/2 (elevation or
118
+ # latitude - spherical, not geodetic).
119
+ def self.inverse(face, chi, psi)
120
+ zeta = 1.0 / Math.sqrt(1.0 + chi**2 + psi**2)
121
+ xi = chi*zeta
122
+ eta = psi*zeta
123
+
124
+ # get the direction cosines
125
+ l, m, n = INVERSE_PARAMETERS[face].call(xi, eta, zeta)
126
+
127
+ [ Math.atan2(m,l), Math.asin(n) ] # φ,θ
128
+ end
129
+
130
+ end # module Tangential
131
+ end # module QuadSphere
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Please mind: in this documentation, and in the code comments, we use
4
+ # φ (phi) to denote longitude, or azimuthal angle, and θ (theta) for
5
+ # latitude, or elevation. This is the convention commonly used in
6
+ # physics. We apologise to all mathheads out there for any
7
+ # inconvenience this may cause.
8
+ module QuadSphere
9
+
10
+ # The identifier of the cube face whose centre maps to θ=π/2
11
+ # (90°N, the North Pole).
12
+ TOP_FACE = 0
13
+
14
+ # The identifier of the cube face whose centre maps to φ=0, θ=0
15
+ # (0° 0°, think Ghana).
16
+ FRONT_FACE = 1
17
+
18
+ # The identifier of the cube face whose centre maps to φ=π/2, θ=0
19
+ # (0° 90°E, think India).
20
+ EAST_FACE = 2
21
+
22
+ # The identifier of the cube face whose centre maps to φ=π, θ=0
23
+ # (0° 180°, think Fiji).
24
+ BACK_FACE = 3
25
+
26
+ # The identifier of the cube face whose centre maps to φ=-π/2, θ=0
27
+ # (0° 90°W, think Central America).
28
+ WEST_FACE = 4
29
+
30
+ # The identifier of the cube face whose centre maps to θ=-π/2
31
+ # (90°S, the South Pole).
32
+ BOTTOM_FACE = 5
33
+
34
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: quad_sphere
3
+ version: !ruby/object:Gem::Version
4
+ hash: 59
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 0
10
+ version: 0.9.0
11
+ platform: ruby
12
+ authors:
13
+ - Cesar Rincon
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-02-02 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: " This is an implementation of the quadrilateralized spherical cube\n projection, an approximately equal-area projection (to within a\n few percent) in which a sphere is projected onto an inscribed\n cube.\n"
22
+ email: crincon@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/quad_sphere.rb
31
+ - lib/quad_sphere/tangential.rb
32
+ - lib/quad_sphere/csc.rb
33
+ homepage: http://rubygems.org/gems/quad_sphere
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ hash: 3
47
+ segments:
48
+ - 0
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.8.25
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Quadrilateralized spherical cube projection
66
+ test_files: []
67
+
68
+ has_rdoc: