├── .gitignore
├── LICENSE
├── README
├── TODO
├── WMM.COF
├── WMM2010.COF
├── cof2Obj.js
├── geomag.htm
├── geomag.js
├── newGeomag.js
├── syncXHR.js
├── testValues.txt
└── wmmViaXHR.js
/.gitignore:
--------------------------------------------------------------------------------
1 | geomag2.js
2 | geomag_2011-09-16.js
3 | geomag2.htm
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Online demo: http://cmweiss.github.io/geomagjs/geomag.htm
2 |
3 | Adapted from the geomagc software and World Magnetic Model of the NOAA Satellite and Information Service, National Geophysical Data Center
4 | http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml
5 |
6 | geoMagFactory() requires a world magnetic model (WMM) object. The helper function cof2Obj(), available in cof2Obj.js, takes the text of WMM.COF and returns an object suitable for geoMagFactory(). A syncronous XMLHttpRequest to fetch the WMM.COF is recommended in a web environment. The helper function syncXHR(), available in syncXHR.js, takes the url of the WMM.COF file and returns the WMM.COF file as text.
7 |
8 | Usage:
9 | geoMagFactory(wmm) returns a function which can compute the Earth's magnetic field.
10 | The returned function requires two arguments, latitude and longitude (in decimal degrees), and, optionally, altitude in feet (default is 0), and a date object (default is the current system time).
11 |
12 | var cof = syncXHR('http://host/path/WMM.COF'),
13 | wmm = cof2Obj(cof),
14 | geoMag = geoMagFactory(wmm),
15 | latitude = 40.0, // decimal degrees (north is positive)
16 | longitude = -80.0, // decimal degrees (east is positive)
17 | altitude = 0, // feet (optional, default is 0)
18 | time = new Date(2012, 4, 20), // optional, default is the current system time
19 | myGeoMag = geoMag(latitude,longitude,altitude,time),
20 | magneticVariation = myGeoMag.dec, // Geomagnetic declination (variation)
21 | // in decimal degrees
22 | // -- east is positive
23 | magneticDip = myGeoMag.dip, // Geomagnetic dip in decimal degrees
24 | // (down is positive)
25 | magneticFieldIntensity = myGeoMag.ti, // Total Intensity of the
26 | // geomagnetic field in nanoteslas
27 | magneticBH = myGeoMag.bh, // Horizontal Intensity of the geomagnetic
28 | // field in nT
29 | magneticBX = myGeoMag.bx, // North Component of the geomagnetic field in nT
30 | magneticBY = myGeoMag.by, // East Component of the geomagnetic field in nT
31 | magneticBZ = myGeoMag.bz, // Vertical Component of the geomagnetic field
32 | // (down is positive)
33 | lat = myGeoMag.lat, // input latitude
34 | lon = myGeoMag.lon; // input longitude
35 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | - Include model metadata in the returned object
2 | - Warn when computing for dates outside of the epoch
3 | - Use reference ellipsoids other than WGS 1984
4 |
--------------------------------------------------------------------------------
/WMM.COF:
--------------------------------------------------------------------------------
1 | 2015.0 WMM-2015 12/15/2014
2 | 1 0 -29438.5 0.0 10.7 0.0
3 | 1 1 -1501.1 4796.2 17.9 -26.8
4 | 2 0 -2445.3 0.0 -8.6 0.0
5 | 2 1 3012.5 -2845.6 -3.3 -27.1
6 | 2 2 1676.6 -642.0 2.4 -13.3
7 | 3 0 1351.1 0.0 3.1 0.0
8 | 3 1 -2352.3 -115.3 -6.2 8.4
9 | 3 2 1225.6 245.0 -0.4 -0.4
10 | 3 3 581.9 -538.3 -10.4 2.3
11 | 4 0 907.2 0.0 -0.4 0.0
12 | 4 1 813.7 283.4 0.8 -0.6
13 | 4 2 120.3 -188.6 -9.2 5.3
14 | 4 3 -335.0 180.9 4.0 3.0
15 | 4 4 70.3 -329.5 -4.2 -5.3
16 | 5 0 -232.6 0.0 -0.2 0.0
17 | 5 1 360.1 47.4 0.1 0.4
18 | 5 2 192.4 196.9 -1.4 1.6
19 | 5 3 -141.0 -119.4 0.0 -1.1
20 | 5 4 -157.4 16.1 1.3 3.3
21 | 5 5 4.3 100.1 3.8 0.1
22 | 6 0 69.5 0.0 -0.5 0.0
23 | 6 1 67.4 -20.7 -0.2 0.0
24 | 6 2 72.8 33.2 -0.6 -2.2
25 | 6 3 -129.8 58.8 2.4 -0.7
26 | 6 4 -29.0 -66.5 -1.1 0.1
27 | 6 5 13.2 7.3 0.3 1.0
28 | 6 6 -70.9 62.5 1.5 1.3
29 | 7 0 81.6 0.0 0.2 0.0
30 | 7 1 -76.1 -54.1 -0.2 0.7
31 | 7 2 -6.8 -19.4 -0.4 0.5
32 | 7 3 51.9 5.6 1.3 -0.2
33 | 7 4 15.0 24.4 0.2 -0.1
34 | 7 5 9.3 3.3 -0.4 -0.7
35 | 7 6 -2.8 -27.5 -0.9 0.1
36 | 7 7 6.7 -2.3 0.3 0.1
37 | 8 0 24.0 0.0 0.0 0.0
38 | 8 1 8.6 10.2 0.1 -0.3
39 | 8 2 -16.9 -18.1 -0.5 0.3
40 | 8 3 -3.2 13.2 0.5 0.3
41 | 8 4 -20.6 -14.6 -0.2 0.6
42 | 8 5 13.3 16.2 0.4 -0.1
43 | 8 6 11.7 5.7 0.2 -0.2
44 | 8 7 -16.0 -9.1 -0.4 0.3
45 | 8 8 -2.0 2.2 0.3 0.0
46 | 9 0 5.4 0.0 0.0 0.0
47 | 9 1 8.8 -21.6 -0.1 -0.2
48 | 9 2 3.1 10.8 -0.1 -0.1
49 | 9 3 -3.1 11.7 0.4 -0.2
50 | 9 4 0.6 -6.8 -0.5 0.1
51 | 9 5 -13.3 -6.9 -0.2 0.1
52 | 9 6 -0.1 7.8 0.1 0.0
53 | 9 7 8.7 1.0 0.0 -0.2
54 | 9 8 -9.1 -3.9 -0.2 0.4
55 | 9 9 -10.5 8.5 -0.1 0.3
56 | 10 0 -1.9 0.0 0.0 0.0
57 | 10 1 -6.5 3.3 0.0 0.1
58 | 10 2 0.2 -0.3 -0.1 -0.1
59 | 10 3 0.6 4.6 0.3 0.0
60 | 10 4 -0.6 4.4 -0.1 0.0
61 | 10 5 1.7 -7.9 -0.1 -0.2
62 | 10 6 -0.7 -0.6 -0.1 0.1
63 | 10 7 2.1 -4.1 0.0 -0.1
64 | 10 8 2.3 -2.8 -0.2 -0.2
65 | 10 9 -1.8 -1.1 -0.1 0.1
66 | 10 10 -3.6 -8.7 -0.2 -0.1
67 | 11 0 3.1 0.0 0.0 0.0
68 | 11 1 -1.5 -0.1 0.0 0.0
69 | 11 2 -2.3 2.1 -0.1 0.1
70 | 11 3 2.1 -0.7 0.1 0.0
71 | 11 4 -0.9 -1.1 0.0 0.1
72 | 11 5 0.6 0.7 0.0 0.0
73 | 11 6 -0.7 -0.2 0.0 0.0
74 | 11 7 0.2 -2.1 0.0 0.1
75 | 11 8 1.7 -1.5 0.0 0.0
76 | 11 9 -0.2 -2.5 0.0 -0.1
77 | 11 10 0.4 -2.0 -0.1 0.0
78 | 11 11 3.5 -2.3 -0.1 -0.1
79 | 12 0 -2.0 0.0 0.1 0.0
80 | 12 1 -0.3 -1.0 0.0 0.0
81 | 12 2 0.4 0.5 0.0 0.0
82 | 12 3 1.3 1.8 0.1 -0.1
83 | 12 4 -0.9 -2.2 -0.1 0.0
84 | 12 5 0.9 0.3 0.0 0.0
85 | 12 6 0.1 0.7 0.1 0.0
86 | 12 7 0.5 -0.1 0.0 0.0
87 | 12 8 -0.4 0.3 0.0 0.0
88 | 12 9 -0.4 0.2 0.0 0.0
89 | 12 10 0.2 -0.9 0.0 0.0
90 | 12 11 -0.9 -0.2 0.0 0.0
91 | 12 12 0.0 0.7 0.0 0.0
92 | 999999999999999999999999999999999999999999999999
93 | 999999999999999999999999999999999999999999999999
94 |
--------------------------------------------------------------------------------
/WMM2010.COF:
--------------------------------------------------------------------------------
1 | 2010.0 WMM-2010 11/20/2009
2 | 1 0 -29496.6 0.0 11.6 0.0
3 | 1 1 -1586.3 4944.4 16.5 -25.9
4 | 2 0 -2396.6 0.0 -12.1 0.0
5 | 2 1 3026.1 -2707.7 -4.4 -22.5
6 | 2 2 1668.6 -576.1 1.9 -11.8
7 | 3 0 1340.1 0.0 0.4 0.0
8 | 3 1 -2326.2 -160.2 -4.1 7.3
9 | 3 2 1231.9 251.9 -2.9 -3.9
10 | 3 3 634.0 -536.6 -7.7 -2.6
11 | 4 0 912.6 0.0 -1.8 0.0
12 | 4 1 808.9 286.4 2.3 1.1
13 | 4 2 166.7 -211.2 -8.7 2.7
14 | 4 3 -357.1 164.3 4.6 3.9
15 | 4 4 89.4 -309.1 -2.1 -0.8
16 | 5 0 -230.9 0.0 -1.0 0.0
17 | 5 1 357.2 44.6 0.6 0.4
18 | 5 2 200.3 188.9 -1.8 1.8
19 | 5 3 -141.1 -118.2 -1.0 1.2
20 | 5 4 -163.0 0.0 0.9 4.0
21 | 5 5 -7.8 100.9 1.0 -0.6
22 | 6 0 72.8 0.0 -0.2 0.0
23 | 6 1 68.6 -20.8 -0.2 -0.2
24 | 6 2 76.0 44.1 -0.1 -2.1
25 | 6 3 -141.4 61.5 2.0 -0.4
26 | 6 4 -22.8 -66.3 -1.7 -0.6
27 | 6 5 13.2 3.1 -0.3 0.5
28 | 6 6 -77.9 55.0 1.7 0.9
29 | 7 0 80.5 0.0 0.1 0.0
30 | 7 1 -75.1 -57.9 -0.1 0.7
31 | 7 2 -4.7 -21.1 -0.6 0.3
32 | 7 3 45.3 6.5 1.3 -0.1
33 | 7 4 13.9 24.9 0.4 -0.1
34 | 7 5 10.4 7.0 0.3 -0.8
35 | 7 6 1.7 -27.7 -0.7 -0.3
36 | 7 7 4.9 -3.3 0.6 0.3
37 | 8 0 24.4 0.0 -0.1 0.0
38 | 8 1 8.1 11.0 0.1 -0.1
39 | 8 2 -14.5 -20.0 -0.6 0.2
40 | 8 3 -5.6 11.9 0.2 0.4
41 | 8 4 -19.3 -17.4 -0.2 0.4
42 | 8 5 11.5 16.7 0.3 0.1
43 | 8 6 10.9 7.0 0.3 -0.1
44 | 8 7 -14.1 -10.8 -0.6 0.4
45 | 8 8 -3.7 1.7 0.2 0.3
46 | 9 0 5.4 0.0 -0.0 0.0
47 | 9 1 9.4 -20.5 -0.1 -0.0
48 | 9 2 3.4 11.5 0.0 -0.2
49 | 9 3 -5.2 12.8 0.3 0.0
50 | 9 4 3.1 -7.2 -0.4 -0.1
51 | 9 5 -12.4 -7.4 -0.3 0.1
52 | 9 6 -0.7 8.0 0.1 -0.0
53 | 9 7 8.4 2.1 -0.1 -0.2
54 | 9 8 -8.5 -6.1 -0.4 0.3
55 | 9 9 -10.1 7.0 -0.2 0.2
56 | 10 0 -2.0 0.0 0.0 0.0
57 | 10 1 -6.3 2.8 -0.0 0.1
58 | 10 2 0.9 -0.1 -0.1 -0.1
59 | 10 3 -1.1 4.7 0.2 0.0
60 | 10 4 -0.2 4.4 -0.0 -0.1
61 | 10 5 2.5 -7.2 -0.1 -0.1
62 | 10 6 -0.3 -1.0 -0.2 -0.0
63 | 10 7 2.2 -3.9 0.0 -0.1
64 | 10 8 3.1 -2.0 -0.1 -0.2
65 | 10 9 -1.0 -2.0 -0.2 0.0
66 | 10 10 -2.8 -8.3 -0.2 -0.1
67 | 11 0 3.0 0.0 0.0 0.0
68 | 11 1 -1.5 0.2 0.0 -0.0
69 | 11 2 -2.1 1.7 -0.0 0.1
70 | 11 3 1.7 -0.6 0.1 0.0
71 | 11 4 -0.5 -1.8 -0.0 0.1
72 | 11 5 0.5 0.9 0.0 0.0
73 | 11 6 -0.8 -0.4 -0.0 0.1
74 | 11 7 0.4 -2.5 -0.0 0.0
75 | 11 8 1.8 -1.3 -0.0 -0.1
76 | 11 9 0.1 -2.1 0.0 -0.1
77 | 11 10 0.7 -1.9 -0.1 -0.0
78 | 11 11 3.8 -1.8 -0.0 -0.1
79 | 12 0 -2.2 0.0 -0.0 0.0
80 | 12 1 -0.2 -0.9 0.0 -0.0
81 | 12 2 0.3 0.3 0.1 0.0
82 | 12 3 1.0 2.1 0.1 -0.0
83 | 12 4 -0.6 -2.5 -0.1 0.0
84 | 12 5 0.9 0.5 -0.0 -0.0
85 | 12 6 -0.1 0.6 0.0 0.1
86 | 12 7 0.5 -0.0 0.0 0.0
87 | 12 8 -0.4 0.1 -0.0 0.0
88 | 12 9 -0.4 0.3 0.0 -0.0
89 | 12 10 0.2 -0.9 0.0 -0.0
90 | 12 11 -0.8 -0.2 -0.1 0.0
91 | 12 12 0.0 0.9 0.1 0.0
92 | 999999999999999999999999999999999999999999999999
93 | 999999999999999999999999999999999999999999999999
94 |
--------------------------------------------------------------------------------
/cof2Obj.js:
--------------------------------------------------------------------------------
1 | /*
2 | cof2Obj.js
3 | Converts the WMM.COF text to a JSON object usable by geoMagFactory().
4 | */
5 |
6 | function cof2Obj(cof) {
7 | 'use strict';
8 | var modelLines = cof.split('\n'),
9 | wmm = [],
10 | i, vals, epoch, model, modelDate;
11 | for (i in modelLines) {
12 | if (modelLines.hasOwnProperty(i)) {
13 | vals = modelLines[i].replace(/^\s+|\s+$/g, "").split(/\s+/);
14 | if (vals.length === 3) {
15 | epoch = parseFloat(vals[0]);
16 | model = vals[1];
17 | modelDate = vals[2];
18 | } else if (vals.length === 6) {
19 | wmm.push({
20 | n: parseInt(vals[0], 10),
21 | m: parseInt(vals[1], 10),
22 | gnm: parseFloat(vals[2]),
23 | hnm: parseFloat(vals[3]),
24 | dgnm: parseFloat(vals[4]),
25 | dhnm: parseFloat(vals[5])
26 | });
27 | }
28 | }
29 | }
30 |
31 | return {epoch: epoch, model: model, modelDate: modelDate, wmm: wmm};
32 | }
33 |
--------------------------------------------------------------------------------
/geomag.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Geomag Library Demo and Test
6 |
22 |
23 |
24 |
25 |
157 |
158 |
159 | Geomag Library Demo and Test
160 | This is a simple page to demonstrate the
161 | geomag.js
162 | library which depends on a world magnetic model
163 | coefficient file.
164 | The coefficient file and more information are available at
165 | The World
166 | Magnetic Model site of NOAA's National Geophysical Data Center.
167 | The source code repository for the geomag.js library is at
168 | https://github.com/cmweiss/geomagJS.
169 |
170 |
197 |
198 | Test |
199 |
200 |
201 | World Magnetic Model (WMM2015) - Test values (pdf)
204 | |
205 |
206 |
207 | |
208 | Date |
209 | Alt (km) |
210 | Lat (deg) |
211 | Lon (deg) |
212 | D (deg) |
213 | I (deg) |
214 | H (nT) |
215 | X (nT) |
216 | Y (nT) |
217 | Z (nT) |
218 | F (nT) |
219 | GV (deg) |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
--------------------------------------------------------------------------------
/geomag.js:
--------------------------------------------------------------------------------
1 | /* 2012-03-26
2 | Copyright 2012 Christopher Weiss (cmweiss@gmail.com)
3 |
4 | Suggestions for improvements are appreciated.
5 |
6 | Adapted from the geomagc software and World Magnetic Model of the NOAA
7 | Satellite and Information Service, National Geophysical Data Center
8 | http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml
9 |
10 | geoMagFactory() requires a world magnetic model (WMM) object. The helper
11 | function cof2Obj(), available in cof2Obj.js, takes the text of WMM.COF and
12 | returns an object suitable for geoMagFactory(). A syncronous XMLHttpRequest
13 | to fetch the WMM.COF is recommended in a web environment. The helper
14 | function syncXHR(), available in syncXHR.js, takes the url of the WMM.COF
15 | file and returns the WMM.COF file as text.
16 |
17 | Usage:
18 | geoMagFactory(wmm) returns a function which can compute the Earth's
19 | magnetic field.
20 | The returned function requires two arguments, latitude and longitude (in
21 | decimal degrees), and, optionally, altitude in feet (default is 0), and
22 | a date object (default is the current system time).
23 |
24 | var cof = syncXHR('http://host/path/WMM.COF'),
25 | wmm = cof2Obj(cof),
26 | geoMag = geoMagFactory(wmm),
27 | latitude = 40.0, // decimal degrees (north is positive)
28 | longitude = -80.0, // decimal degrees (east is positive)
29 | altitude = 0, // feet (optional, default is 0)
30 | time = new Date(2012, 4, 20), // (optional, default is the current
31 | // system time)
32 | myGeoMag = geoMag(latitude, longitude, altitude, time),
33 | magneticVariation = myGeoMag.dec, // Geomagnetic declination
34 | // (variation) in decimal degrees
35 | // -- east is positive
36 | magneticDip = myGeoMag.dip, // Geomagnetic dip in decimal degrees
37 | // (down is positive)
38 | magneticFieldIntensity = myGeoMag.ti, // Total intensity of the
39 | // geomagnetic field in
40 | // nanoteslas
41 | magneticBH = myGeoMag.bh, // Horizontal intensity of the geomagnetic
42 | // field in nT
43 | magneticBX = myGeoMag.bx, // North component of the geomagnetic field
44 | // in nT
45 | magneticBY = myGeoMag.by, // East component of the geomagnetic field
46 | // in nT
47 | magneticBZ = myGeoMag.bz, // Vertical component of the geomagnetic
48 | // field (down is positive)
49 | lat = myGeoMag.lat, // input latitude
50 | lon = myGeoMag.lon; // input longitude
51 | */
52 |
53 | /*jslint plusplus: true */
54 | function geoMagFactory(wmm) {
55 | 'use strict';
56 | function rad2deg(rad) {
57 | return rad * (180 / Math.PI);
58 | }
59 | function deg2rad(deg) {
60 | return deg * (Math.PI / 180);
61 | }
62 |
63 | var i, model, epoch = wmm.epoch,
64 | z = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
65 | maxord = 12,
66 | tc = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
67 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
68 | z.slice()],
69 | sp = z.slice(),
70 | cp = z.slice(),
71 | pp = z.slice(),
72 | p = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
73 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
74 | z.slice()],
75 | dp = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
76 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
77 | z.slice()],
78 | a = 6378.137,
79 | b = 6356.7523142,
80 | re = 6371.2,
81 | a2 = a * a,
82 | b2 = b * b,
83 | c2 = a2 - b2,
84 | a4 = a2 * a2,
85 | b4 = b2 * b2,
86 | c4 = a4 - b4,
87 | c = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
88 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
89 | z.slice()],
90 | cd = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
91 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
92 | z.slice()],
93 | n, m,
94 | snorm = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
95 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
96 | z.slice(), z.slice()],
97 | j,
98 | k = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
99 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
100 | z.slice()],
101 | flnmj,
102 | fn = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
103 | fm = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
104 | D2;
105 |
106 | tc[0][0] = 0;
107 | sp[0] = 0.0;
108 | cp[0] = 1.0;
109 | pp[0] = 1.0;
110 | p[0][0] = 1;
111 |
112 | model = wmm.wmm;
113 | for (i in model) {
114 | if (model.hasOwnProperty(i)) {
115 | if (model[i].m <= model[i].n) {
116 | c[model[i].m][model[i].n] = model[i].gnm;
117 | cd[model[i].m][model[i].n] = model[i].dgnm;
118 | if (model[i].m !== 0) {
119 | c[model[i].n][model[i].m - 1] = model[i].hnm;
120 | cd[model[i].n][model[i].m - 1] = model[i].dhnm;
121 | }
122 | }
123 | }
124 | }
125 | wmm = null;
126 | model = null;
127 |
128 | /* CONVERT SCHMIDT NORMALIZED GAUSS COEFFICIENTS TO UNNORMALIZED */
129 | snorm[0][0] = 1;
130 |
131 | for (n = 1; n <= maxord; n++) {
132 | snorm[0][n] = snorm[0][n - 1] * (2 * n - 1) / n;
133 | j = 2;
134 |
135 | for (m = 0, D2 = (n - m + 1); D2 > 0; D2--, m++) {
136 | k[m][n] = (((n - 1) * (n - 1)) - (m * m)) /
137 | ((2 * n - 1) * (2 * n - 3));
138 | if (m > 0) {
139 | flnmj = ((n - m + 1) * j) / (n + m);
140 | snorm[m][n] = snorm[m - 1][n] * Math.sqrt(flnmj);
141 | j = 1;
142 | c[n][m - 1] = snorm[m][n] * c[n][m - 1];
143 | cd[n][m - 1] = snorm[m][n] * cd[n][m - 1];
144 | }
145 | c[m][n] = snorm[m][n] * c[m][n];
146 | cd[m][n] = snorm[m][n] * cd[m][n];
147 | }
148 | }
149 | k[1][1] = 0.0;
150 |
151 | return function (glat, glon, h, date) {
152 | function decimalDate(date) {
153 | date = date || new Date();
154 | var year = date.getUTCFullYear(),
155 | daysInYear = 365 +
156 | (((year % 400 === 0) || (year % 4 === 0 && (year % 100 > 0))) ? 1 : 0),
157 | msInYear = daysInYear * 24 * 60 * 60 * 1000;
158 |
159 | return date.getUTCFullYear() + (date.valueOf() - Date.UTC(year, 0)) / msInYear;
160 | }
161 |
162 | var alt = (h / 3280.8399) || 0, // convert h (in feet) to kilometers or set default of 0
163 | time = decimalDate(date),
164 | dt = time - epoch,
165 | rlat = deg2rad(glat),
166 | rlon = deg2rad(glon),
167 | srlon = Math.sin(rlon),
168 | srlat = Math.sin(rlat),
169 | crlon = Math.cos(rlon),
170 | crlat = Math.cos(rlat),
171 | srlat2 = srlat * srlat,
172 | crlat2 = crlat * crlat,
173 | q,
174 | q1,
175 | q2,
176 | ct,
177 | st,
178 | r2,
179 | r,
180 | d,
181 | ca,
182 | sa,
183 | aor,
184 | ar,
185 | br = 0.0,
186 | bt = 0.0,
187 | bp = 0.0,
188 | bpp = 0.0,
189 | par,
190 | temp1,
191 | temp2,
192 | parp,
193 | D4,
194 | bx,
195 | by,
196 | bz,
197 | bh,
198 | ti,
199 | dec,
200 | dip,
201 | gv;
202 | sp[1] = srlon;
203 | cp[1] = crlon;
204 |
205 | /* CONVERT FROM GEODETIC COORDS. TO SPHERICAL COORDS. */
206 | q = Math.sqrt(a2 - c2 * srlat2);
207 | q1 = alt * q;
208 | q2 = ((q1 + a2) / (q1 + b2)) * ((q1 + a2) / (q1 + b2));
209 | ct = srlat / Math.sqrt(q2 * crlat2 + srlat2);
210 | st = Math.sqrt(1.0 - (ct * ct));
211 | r2 = (alt * alt) + 2.0 * q1 + (a4 - c4 * srlat2) / (q * q);
212 | r = Math.sqrt(r2);
213 | d = Math.sqrt(a2 * crlat2 + b2 * srlat2);
214 | ca = (alt + d) / r;
215 | sa = c2 * crlat * srlat / (r * d);
216 |
217 | for (m = 2; m <= maxord; m++) {
218 | sp[m] = sp[1] * cp[m - 1] + cp[1] * sp[m - 1];
219 | cp[m] = cp[1] * cp[m - 1] - sp[1] * sp[m - 1];
220 | }
221 |
222 | aor = re / r;
223 | ar = aor * aor;
224 |
225 | for (n = 1; n <= maxord; n++) {
226 | ar = ar * aor;
227 | for (m = 0, D4 = (n + m + 1); D4 > 0; D4--, m++) {
228 |
229 | /*
230 | COMPUTE UNNORMALIZED ASSOCIATED LEGENDRE POLYNOMIALS
231 | AND DERIVATIVES VIA RECURSION RELATIONS
232 | */
233 | if (n === m) {
234 | p[m][n] = st * p[m - 1][n - 1];
235 | dp[m][n] = st * dp[m - 1][n - 1] + ct *
236 | p[m - 1][n - 1];
237 | } else if (n === 1 && m === 0) {
238 | p[m][n] = ct * p[m][n - 1];
239 | dp[m][n] = ct * dp[m][n - 1] - st * p[m][n - 1];
240 | } else if (n > 1 && n !== m) {
241 | if (m > n - 2) { p[m][n - 2] = 0; }
242 | if (m > n - 2) { dp[m][n - 2] = 0.0; }
243 | p[m][n] = ct * p[m][n - 1] - k[m][n] * p[m][n - 2];
244 | dp[m][n] = ct * dp[m][n - 1] - st * p[m][n - 1] -
245 | k[m][n] * dp[m][n - 2];
246 | }
247 |
248 | /*
249 | TIME ADJUST THE GAUSS COEFFICIENTS
250 | */
251 |
252 | tc[m][n] = c[m][n] + dt * cd[m][n];
253 | if (m !== 0) {
254 | tc[n][m - 1] = c[n][m - 1] + dt * cd[n][m - 1];
255 | }
256 |
257 | /*
258 | ACCUMULATE TERMS OF THE SPHERICAL HARMONIC EXPANSIONS
259 | */
260 | par = ar * p[m][n];
261 | if (m === 0) {
262 | temp1 = tc[m][n] * cp[m];
263 | temp2 = tc[m][n] * sp[m];
264 | } else {
265 | temp1 = tc[m][n] * cp[m] + tc[n][m - 1] * sp[m];
266 | temp2 = tc[m][n] * sp[m] - tc[n][m - 1] * cp[m];
267 | }
268 | bt = bt - ar * temp1 * dp[m][n];
269 | bp += (fm[m] * temp2 * par);
270 | br += (fn[n] * temp1 * par);
271 | /*
272 | SPECIAL CASE: NORTH/SOUTH GEOGRAPHIC POLES
273 | */
274 | if (st === 0.0 && m === 1) {
275 | if (n === 1) {
276 | pp[n] = pp[n - 1];
277 | } else {
278 | pp[n] = ct * pp[n - 1] - k[m][n] * pp[n - 2];
279 | }
280 | parp = ar * pp[n];
281 | bpp += (fm[m] * temp2 * parp);
282 | }
283 | }
284 | }
285 |
286 | bp = (st === 0.0 ? bpp : bp / st);
287 | /*
288 | ROTATE MAGNETIC VECTOR COMPONENTS FROM SPHERICAL TO
289 | GEODETIC COORDINATES
290 | */
291 | bx = -bt * ca - br * sa;
292 | by = bp;
293 | bz = bt * sa - br * ca;
294 |
295 | /*
296 | COMPUTE DECLINATION (DEC), INCLINATION (DIP) AND
297 | TOTAL INTENSITY (TI)
298 | */
299 | bh = Math.sqrt((bx * bx) + (by * by));
300 | ti = Math.sqrt((bh * bh) + (bz * bz));
301 | dec = rad2deg(Math.atan2(by, bx));
302 | dip = rad2deg(Math.atan2(bz, bh));
303 |
304 | /*
305 | COMPUTE MAGNETIC GRID VARIATION IF THE CURRENT
306 | GEODETIC POSITION IS IN THE ARCTIC OR ANTARCTIC
307 | (I.E. GLAT > +55 DEGREES OR GLAT < -55 DEGREES)
308 | OTHERWISE, SET MAGNETIC GRID VARIATION TO -999.0
309 | */
310 |
311 | if (Math.abs(glat) >= 55.0) {
312 | if (glat > 0.0 && glon >= 0.0) {
313 | gv = dec - glon;
314 | } else if (glat > 0.0 && glon < 0.0) {
315 | gv = dec + Math.abs(glon);
316 | } else if (glat < 0.0 && glon >= 0.0) {
317 | gv = dec + glon;
318 | } else if (glat < 0.0 && glon < 0.0) {
319 | gv = dec - Math.abs(glon);
320 | }
321 | if (gv > 180.0) {
322 | gv -= 360.0;
323 | } else if (gv < -180.0) { gv += 360.0; }
324 | }
325 |
326 | return {dec: dec, dip: dip, ti: ti, bh: bh, bx: bx, by: by, bz: bz, lat: glat, lon: glon, gv: gv, epoch: epoch};
327 | };
328 | }
329 |
--------------------------------------------------------------------------------
/newGeomag.js:
--------------------------------------------------------------------------------
1 | /*jslint plusplus:true */
2 | function Geomag(model) {
3 | 'use strict';
4 | var wmm,
5 | maxord = 12,
6 | a = 6378.137, // WGS 1984 Equatorial axis (km)
7 | b = 6356.7523142, // WGS 1984 Polar axis (km)
8 | re = 6371.2,
9 | a2 = a * a,
10 | b2 = b * b,
11 | c2 = a2 - b2,
12 | a4 = a2 * a2,
13 | b4 = b2 * b2,
14 | c4 = a4 - b4,
15 | z = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
16 | unnormalizedWMM;
17 |
18 | function parseCof(cof) {
19 | wmm = (function (cof) {
20 | var modelLines = cof.split('\n'), wmm = [], i, vals, epoch, model, modelDate;
21 | for (i in modelLines) {
22 | if (modelLines.hasOwnProperty(i)) {
23 | vals = modelLines[i].replace(/^\s+|\s+$/g, "").split(/\s+/);
24 | if (vals.length === 3) {
25 | epoch = parseFloat(vals[0]);
26 | model = vals[1];
27 | modelDate = vals[2];
28 | } else if (vals.length === 6) {
29 | wmm.push({
30 | n: parseInt(vals[0], 10),
31 | m: parseInt(vals[1], 10),
32 | gnm: parseFloat(vals[2]),
33 | hnm: parseFloat(vals[3]),
34 | dgnm: parseFloat(vals[4]),
35 | dhnm: parseFloat(vals[5])
36 | });
37 | }
38 | }
39 | }
40 |
41 | return {epoch: epoch, model: model, modelDate: modelDate, wmm: wmm};
42 | }(cof));
43 | }
44 |
45 | function unnormalize(wmm) {
46 | var i, j, m, n, D2, flnmj,
47 | c = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
48 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
49 | z.slice()],
50 | cd = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
51 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
52 | z.slice()],
53 | k = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
54 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
55 | z.slice()],
56 | snorm = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
57 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
58 | z.slice(), z.slice()],
59 | model = wmm.wmm;
60 | for (i in model) {
61 | if (model.hasOwnProperty(i)) {
62 | if (model[i].m <= model[i].n) {
63 | c[model[i].m][model[i].n] = model[i].gnm;
64 | cd[model[i].m][model[i].n] = model[i].dgnm;
65 | if (model[i].m !== 0) {
66 | c[model[i].n][model[i].m - 1] = model[i].hnm;
67 | cd[model[i].n][model[i].m - 1] = model[i].dhnm;
68 | }
69 | }
70 | }
71 | }
72 | /* CONVERT SCHMIDT NORMALIZED GAUSS COEFFICIENTS TO UNNORMALIZED */
73 | snorm[0][0] = 1;
74 |
75 | for (n = 1; n <= maxord; n++) {
76 | snorm[0][n] = snorm[0][n - 1] * (2 * n - 1) / n;
77 | j = 2;
78 |
79 | for (m = 0, D2 = (n - m + 1); D2 > 0; D2--, m++) {
80 | k[m][n] = (((n - 1) * (n - 1)) - (m * m)) /
81 | ((2 * n - 1) * (2 * n - 3));
82 | if (m > 0) {
83 | flnmj = ((n - m + 1) * j) / (n + m);
84 | snorm[m][n] = snorm[m - 1][n] * Math.sqrt(flnmj);
85 | j = 1;
86 | c[n][m - 1] = snorm[m][n] * c[n][m - 1];
87 | cd[n][m - 1] = snorm[m][n] * cd[n][m - 1];
88 | }
89 | c[m][n] = snorm[m][n] * c[m][n];
90 | cd[m][n] = snorm[m][n] * cd[m][n];
91 | }
92 | }
93 | k[1][1] = 0.0;
94 |
95 | unnormalizedWMM = {epoch: wmm.epoch, k: k, c: c, cd: cd};
96 | }
97 |
98 | this.setCof = function (cof) {
99 | parseCof(cof);
100 | unnormalize(wmm);
101 | };
102 | this.getWmm = function () {
103 | return wmm;
104 | };
105 | this.setUnnorm = function (val) {
106 | unnormalizedWMM = val;
107 | };
108 | this.getUnnorm = function () {
109 | return unnormalizedWMM;
110 | };
111 | this.getEpoch = function () {
112 | return unnormalizedWMM.epoch;
113 | };
114 | this.setEllipsoid = function (e) {
115 | a = e.a;
116 | b = e.b;
117 | re = 6371.2;
118 | a2 = a * a;
119 | b2 = b * b;
120 | c2 = a2 - b2;
121 | a4 = a2 * a2;
122 | b4 = b2 * b2;
123 | c4 = a4 - b4;
124 | };
125 | this.getEllipsoid = function () {
126 | return {a: a, b: b};
127 | };
128 | this.calculate = function (glat, glon, h, date) {
129 | if (unnormalizedWMM === undefined) {
130 | throw new Error("A World Magnetic Model has not been set.")
131 | }
132 | if (glat === undefined || glon === undefined) {
133 | throw new Error("Latitude and longitude are required arguments.");
134 | }
135 | function rad2deg(rad) {
136 | return rad * (180 / Math.PI);
137 | }
138 | function deg2rad(deg) {
139 | return deg * (Math.PI / 180);
140 | }
141 | function decimalDate(date) {
142 | date = date || new Date();
143 | var year = date.getFullYear(),
144 | daysInYear = 365 +
145 | (((year % 400 === 0) || (year % 4 === 0 && (year % 100 > 0))) ? 1 : 0),
146 | msInYear = daysInYear * 24 * 60 * 60 * 1000;
147 |
148 | return date.getFullYear() + (date.valueOf() - (new Date(year, 0)).valueOf()) / msInYear;
149 | }
150 |
151 | var epoch = unnormalizedWMM.epoch,
152 | k = unnormalizedWMM.k,
153 | c = unnormalizedWMM.c,
154 | cd = unnormalizedWMM.cd,
155 | alt = (h / 3280.8399) || 0, // convert h (in feet) to kilometers (default, 0 km)
156 | dt = decimalDate(date) - epoch,
157 | rlat = deg2rad(glat),
158 | rlon = deg2rad(glon),
159 | srlon = Math.sin(rlon),
160 | srlat = Math.sin(rlat),
161 | crlon = Math.cos(rlon),
162 | crlat = Math.cos(rlat),
163 | srlat2 = srlat * srlat,
164 | crlat2 = crlat * crlat,
165 | q,
166 | q1,
167 | q2,
168 | ct,
169 | st,
170 | r2,
171 | r,
172 | d,
173 | ca,
174 | sa,
175 | aor,
176 | ar,
177 | br = 0.0,
178 | bt = 0.0,
179 | bp = 0.0,
180 | bpp = 0.0,
181 | par,
182 | temp1,
183 | temp2,
184 | parp,
185 | D4,
186 | m,
187 | n,
188 | fn = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
189 | fm = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
190 | z = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
191 | tc = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
192 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
193 | z.slice()],
194 | sp = z.slice(),
195 | cp = z.slice(),
196 | pp = z.slice(),
197 | p = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
198 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
199 | z.slice()],
200 | dp = [z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
201 | z.slice(), z.slice(), z.slice(), z.slice(), z.slice(), z.slice(),
202 | z.slice()],
203 | bx,
204 | by,
205 | bz,
206 | bh,
207 | ti,
208 | dec,
209 | dip,
210 | gv;
211 | sp[0] = 0.0;
212 | sp[1] = srlon;
213 | cp[1] = crlon;
214 | tc[0][0] = 0;
215 | cp[0] = 1.0;
216 | pp[0] = 1.0;
217 | p[0][0] = 1;
218 |
219 | /* CONVERT FROM GEODETIC COORDS. TO SPHERICAL COORDS. */
220 | q = Math.sqrt(a2 - c2 * srlat2);
221 | q1 = alt * q;
222 | q2 = ((q1 + a2) / (q1 + b2)) * ((q1 + a2) / (q1 + b2));
223 | ct = srlat / Math.sqrt(q2 * crlat2 + srlat2);
224 | st = Math.sqrt(1.0 - (ct * ct));
225 | r2 = (alt * alt) + 2.0 * q1 + (a4 - c4 * srlat2) / (q * q);
226 | r = Math.sqrt(r2);
227 | d = Math.sqrt(a2 * crlat2 + b2 * srlat2);
228 | ca = (alt + d) / r;
229 | sa = c2 * crlat * srlat / (r * d);
230 |
231 | for (m = 2; m <= maxord; m++) {
232 | sp[m] = sp[1] * cp[m - 1] + cp[1] * sp[m - 1];
233 | cp[m] = cp[1] * cp[m - 1] - sp[1] * sp[m - 1];
234 | }
235 |
236 | aor = re / r;
237 | ar = aor * aor;
238 |
239 | for (n = 1; n <= maxord; n++) {
240 | ar = ar * aor;
241 | for (m = 0, D4 = (n + m + 1); D4 > 0; D4--, m++) {
242 |
243 | /*
244 | COMPUTE UNNORMALIZED ASSOCIATED LEGENDRE POLYNOMIALS
245 | AND DERIVATIVES VIA RECURSION RELATIONS
246 | */
247 | if (n === m) {
248 | p[m][n] = st * p[m - 1][n - 1];
249 | dp[m][n] = st * dp[m - 1][n - 1] + ct *
250 | p[m - 1][n - 1];
251 | } else if (n === 1 && m === 0) {
252 | p[m][n] = ct * p[m][n - 1];
253 | dp[m][n] = ct * dp[m][n - 1] - st * p[m][n - 1];
254 | } else if (n > 1 && n !== m) {
255 | if (m > n - 2) { p[m][n - 2] = 0; }
256 | if (m > n - 2) { dp[m][n - 2] = 0.0; }
257 | p[m][n] = ct * p[m][n - 1] - k[m][n] * p[m][n - 2];
258 | dp[m][n] = ct * dp[m][n - 1] - st * p[m][n - 1] -
259 | k[m][n] * dp[m][n - 2];
260 | }
261 |
262 | /*
263 | TIME ADJUST THE GAUSS COEFFICIENTS
264 | */
265 |
266 | tc[m][n] = c[m][n] + dt * cd[m][n];
267 | if (m !== 0) {
268 | tc[n][m - 1] = c[n][m - 1] + dt * cd[n][m - 1];
269 | }
270 |
271 | /*
272 | ACCUMULATE TERMS OF THE SPHERICAL HARMONIC EXPANSIONS
273 | */
274 | par = ar * p[m][n];
275 | if (m === 0) {
276 | temp1 = tc[m][n] * cp[m];
277 | temp2 = tc[m][n] * sp[m];
278 | } else {
279 | temp1 = tc[m][n] * cp[m] + tc[n][m - 1] * sp[m];
280 | temp2 = tc[m][n] * sp[m] - tc[n][m - 1] * cp[m];
281 | }
282 | bt = bt - ar * temp1 * dp[m][n];
283 | bp += (fm[m] * temp2 * par);
284 | br += (fn[n] * temp1 * par);
285 | /*
286 | SPECIAL CASE: NORTH/SOUTH GEOGRAPHIC POLES
287 | */
288 | if (st === 0.0 && m === 1) {
289 | if (n === 1) {
290 | pp[n] = pp[n - 1];
291 | } else {
292 | pp[n] = ct * pp[n - 1] - k[m][n] * pp[n - 2];
293 | }
294 | parp = ar * pp[n];
295 | bpp += (fm[m] * temp2 * parp);
296 | }
297 | }
298 | }
299 |
300 | bp = (st === 0.0 ? bpp : bp / st);
301 | /*
302 | ROTATE MAGNETIC VECTOR COMPONENTS FROM SPHERICAL TO
303 | GEODETIC COORDINATES
304 | */
305 | bx = -bt * ca - br * sa;
306 | by = bp;
307 | bz = bt * sa - br * ca;
308 |
309 | /*
310 | COMPUTE DECLINATION (DEC), INCLINATION (DIP) AND
311 | TOTAL INTENSITY (TI)
312 | */
313 | bh = Math.sqrt((bx * bx) + (by * by));
314 | ti = Math.sqrt((bh * bh) + (bz * bz));
315 | dec = rad2deg(Math.atan2(by, bx));
316 | dip = rad2deg(Math.atan2(bz, bh));
317 |
318 | /*
319 | COMPUTE MAGNETIC GRID VARIATION IF THE CURRENT
320 | GEODETIC POSITION IS IN THE ARCTIC OR ANTARCTIC
321 | (I.E. GLAT > +55 DEGREES OR GLAT < -55 DEGREES)
322 | OTHERWISE, SET MAGNETIC GRID VARIATION TO -999.0
323 | */
324 |
325 | if (Math.abs(glat) >= 55.0) {
326 | if (glat > 0.0 && glon >= 0.0) {
327 | gv = dec - glon;
328 | } else if (glat > 0.0 && glon < 0.0) {
329 | gv = dec + Math.abs(glon);
330 | } else if (glat < 0.0 && glon >= 0.0) {
331 | gv = dec + glon;
332 | } else if (glat < 0.0 && glon < 0.0) {
333 | gv = dec - Math.abs(glon);
334 | }
335 | if (gv > 180.0) {
336 | gv -= 360.0;
337 | } else if (gv < -180.0) { gv += 360.0; }
338 | }
339 |
340 | return {dec: dec, dip: dip, ti: ti, bh: bh, bx: bx, by: by, bz: bz, lat: glat, lon: glon, gv: gv};
341 | };
342 | this.calc = this.calculate;
343 | this.mag = this.calculate;
344 |
345 | if (model !== undefined) { // initialize
346 | if (typeof model === 'string') { // WMM.COF file
347 | parseCof(model);
348 | unnormalize(wmm);
349 | } else if (typeof model === 'object') { // unnorm obj
350 | this.setUnnorm(model);
351 | } else {
352 | throw new Error("Invalid argument type");
353 | }
354 | }
355 | }
356 |
--------------------------------------------------------------------------------
/syncXHR.js:
--------------------------------------------------------------------------------
1 | /*
2 | Syncronous XML HTTP Request
3 |
4 | Returns the contents of the file at the specified URL.
5 | */
6 |
7 | /*jslint devel: true, browser: true, windows: true */
8 | if (window.XMLHttpRequest === undefined) {
9 | window.XMLHttpRequest = function() {
10 | try {
11 | return new ActiveXObject("Msxml2.XMLHTTP.6.0");
12 | } catch (e1) {
13 | try {
14 | return new ActiveXObject("Msxml2.XMLHTTP.3.0");
15 | } catch (e2) {
16 | throw new Error("XMLHttpRequest is not supported");
17 | }
18 | }
19 | };
20 | }
21 |
22 | function syncXHR(url) {
23 | 'use strict';
24 | var xmlHttp = new XMLHttpRequest();
25 |
26 | xmlHttp.open("GET", url, false);
27 | if ("overrideMimeType" in xmlHttp) {xmlHttp.overrideMimeType("text/plain");}
28 | xmlHttp.send(null);
29 |
30 | return (xmlHttp.status === 200 || xmlHttp.readyState === 4) ? xmlHttp.responseText : false;
31 | }
32 |
--------------------------------------------------------------------------------
/testValues.txt:
--------------------------------------------------------------------------------
1 | WMM2015
2 | Date Alt(km) Lat(deg) Lon(deg) D(deg) I(deg) H(nT) X(nT) Y(nT) Z(nT) F(nT) GV(deg)
3 | 2015.0 0 80 0 -3.85 83.04 6642.1 6627.1 -445.9 54432.3 54836.0 -3.85
4 | 2015.0 0 0 120 0.57 -15.89 39520.2 39518.2 392.9 -11252.4 41090.9 0.57
5 | 2015.0 0 -80 240 69.81 -72.39 16793.5 5797.3 15761.1 -52919.1 55519.8 -50.19
6 | 2015.0 100 80 0 -4.27 83.09 6331.9 6314.3 -471.6 52269.8 52652.0 -4.27
7 | 2015.0 100 0 120 0.56 -16.01 37537.3 37535.6 364.4 -10773.4 39052.7 0.56
8 | 2015.0 100 -80 240 69.22 -72.57 15820.7 5613.1 14791.5 -50378.6 52804.4 -50.78
9 | 2017.5 0 80 0 -2.75 83.08 6607.0 6599.4 -317.1 54459.2 54858.5 -2.75
10 | 2017.5 0 0 120 0.32 -15.57 39572.0 39571.4 222.5 -11030.1 41080.5 0.32
11 | 2017.5 0 -80 240 69.58 -72.28 16839.1 5873.8 15781.4 -52687.9 55313.4 -50.42
12 | 2017.5 100 80 0 -3.17 83.13 6300.1 6290.5 -348.5 52292.7 52670.9 -3.17
13 | 2017.5 100 0 120 0.32 -15.70 37586.1 37585.5 209.5 -10564.2 39042.5 0.32
14 | 2017.5 100 -80 240 69.00 -72.45 15862.0 5683.5 14808.8 -50163.0 52611.1 -51.00
15 |
--------------------------------------------------------------------------------
/wmmViaXHR.js:
--------------------------------------------------------------------------------
1 | function wmmViaXHR(url) {
2 | var xmlHttp, modelLines, wmm, i, vals, epoch, model, modelDate, temp;
3 | try {
4 | xmlHttp = new XMLHttpRequest();
5 | } catch (e0) {
6 | try {// Internet Explorer 5 & 6
7 | xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
8 | } catch (e1) {
9 | try {
10 | xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
11 | } catch (e2) {
12 | alert("Your browser does not support AJAX!");
13 | return false;
14 | }
15 | }
16 | }
17 | xmlHttp.open("GET", url, false);
18 | xmlHttp.send(null);
19 | if (xmlHttp.status === 200 || xmlHttp.readyState === 4) {
20 | modelLines = xmlHttp.responseText.split('\n');
21 | wmm = [];
22 | for (i in modelLines) {
23 | if (modelLines.hasOwnProperty(i)) {
24 | vals = modelLines[i].replace(/^\s+|\s+$/g, "").split(/\s+/);
25 | if (vals.length === 3) {
26 | epoch = parseFloat(vals[0]);
27 | model = vals[1];
28 | modelDate = vals[2];
29 | } else if (vals.length === 6) {
30 | temp = {
31 | n: parseInt(vals[0], 10),
32 | m: parseInt(vals[1], 10),
33 | gnm: parseFloat(vals[2]),
34 | hnm: parseFloat(vals[3]),
35 | dgnm: parseFloat(vals[4]),
36 | dhnm: parseFloat(vals[5])
37 | };
38 | wmm.push(temp);
39 | }
40 | }
41 | }
42 | modelLines = null;
43 | xmlHttp = null;
44 |
45 | return {epoch: epoch, model: model, modelDate: modelDate, wmm: wmm};
46 | } else {
47 | return false;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------