├── .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 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 |
Latitude:(decimal degrees - north is positive)
Longitude:(decimal degrees - east is positive)
Altitude:(feet)
Variation:(decimal degrees - east is positive)
197 | 198 | 199 | 200 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 |
Test
201 | World Magnetic Model (WMM2015) - Test values (pdf) 204 |
DateAlt (km)Lat (deg)Lon (deg)D (deg)I (deg)H (nT)X (nT)Y (nT)Z (nT)F (nT)GV (deg)
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 | --------------------------------------------------------------------------------