├── .gitignore
├── CMakeLists.txt
├── HOWTO-RELEASE.txt
├── LICENSE.txt
├── README.md
├── distrib-JavaScript
└── 00README.md
├── dms
├── CMakeLists.txt
├── DMS.js
├── HEAD.js.in
├── README.md
├── TAIL.js
├── package.json.in
├── test
│ └── dmstest.js
└── types
│ └── geographiclib-dms.d.ts
├── doc
├── .htaccess
├── CMakeLists.txt
├── FOOTER.html
├── GeographicLib.md.in
├── HEADER.html
├── conf.json
└── tutorials
│ ├── 1-geodesics.md
│ ├── 2-interface.md
│ ├── 3-examples.md
│ └── tutorials.json
├── geodesic
├── CMakeLists.txt
├── Geodesic.js
├── GeodesicLine.js
├── HEAD.js.in
├── Math.js.in
├── PolygonArea.js
├── README.md
├── TAIL.js
├── package.json.in
├── test
│ ├── geodesictest.js
│ └── signtest.js
└── types
│ └── geographiclib-geodesic.d.ts
└── samples
├── CMakeLists.txt
├── geod-calc.html
├── geod-google-instructions.html
└── geod-google.html
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | BUILD*
3 | distrib-JavaScript/
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 3.13.0)
2 | project (GeographicLib-JavaScript NONE)
3 | set (GEOD_PROJECT geographiclib-geodesic)
4 | set (DMS_PROJECT geographiclib-dms)
5 |
6 | # Version information
7 | set (PROJECT_VERSION_MAJOR 2)
8 | set (PROJECT_VERSION_MINOR 1)
9 | set (PROJECT_VERSION_PATCH 1)
10 | # Always include patch number in version
11 | set (PROJECT_VERSION
12 | "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
13 | set (PROJECT_VERSION_SUFFIX "")
14 | set (PROJECT_FULLVERSION "${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX}")
15 |
16 | set (RELEASE_DATE "2024-08-18")
17 |
18 | # Set a default build type for single-configuration cmake generators if
19 | # no build type is set.
20 | if (NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
21 | set (CMAKE_BUILD_TYPE Release)
22 | endif ()
23 |
24 | set (MODDIR "lib/node_modules"
25 | CACHE STRING "Installation directory for GeographicLib-JavaScript module")
26 | option (BUILD_DOCUMENTATION "Use doxygen to create the documentation" OFF)
27 |
28 | find_program (MINIFY jsmin REQUIRED)
29 | find_program (LINT jshint)
30 | find_program (MOCHA mocha)
31 | find_program (RSYNC rsync)
32 | find_program (NPM npm)
33 |
34 | set (DESTGEOD ${PROJECT_BINARY_DIR}/node_modules/${GEOD_PROJECT})
35 | set (PACKAGEDGEODJS ${DESTGEOD}/${GEOD_PROJECT}.js)
36 | set (PACKAGEDGEODMINJS ${DESTGEOD}/${GEOD_PROJECT}.min.js)
37 | set (PACKGEOD
38 | "${PROJECT_BINARY_DIR}/${GEOD_PROJECT}-${PROJECT_FULLVERSION}.tgz")
39 | set (DESTDMS ${PROJECT_BINARY_DIR}/node_modules/${DMS_PROJECT})
40 | set (PACKAGEDDMSJS ${DESTDMS}/${DMS_PROJECT}.js)
41 | set (PACKAGEDDMSMINJS ${DESTDMS}/${DMS_PROJECT}.min.js)
42 | set (PACKDMS "${PROJECT_BINARY_DIR}/${DMS_PROJECT}-${PROJECT_FULLVERSION}.tgz")
43 |
44 | enable_testing ()
45 |
46 | add_subdirectory (geodesic)
47 | add_subdirectory (dms)
48 | add_subdirectory (samples)
49 |
50 | if (BUILD_DOCUMENTATION)
51 | # For JavaScript documentation
52 | find_program (JSDOC jsdoc REQUIRED)
53 | add_subdirectory (doc)
54 | endif ()
55 |
56 | if (LINT)
57 | add_custom_target (lint)
58 | add_dependencies (lint lintgeodesic lintdms)
59 | endif ()
60 |
61 | if (NPM)
62 | add_custom_command (OUTPUT ${PACKGEOD} ${PACKDMS}
63 | COMMAND ${NPM} pack ${DESTGEOD} ${DESTDMS}
64 | DEPENDS ${PACKAGEDGEODJS} ${PACKAGEDDMSJS}
65 | ${DESTGEOD}/package.json ${DESTDMS}/package.json
66 | VERBATIM)
67 | add_custom_target (pack ALL
68 | DEPENDS ${PACKGEOD} ${PACKDMS} bundlegeod bundledms)
69 | if (RSYNC)
70 | set (USER karney)
71 | set (DATAROOT $ENV{HOME}/web/geographiclib-files/distrib-JavaScript)
72 | set (FRSDEPLOY ${USER}@frs.sourceforge.net:/home/frs/project/geographiclib)
73 | add_custom_target (stage-dist
74 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
75 | ${PACKGEOD} ${PACKDMS} ${PROJECT_SOURCE_DIR}/distrib-JavaScript/
76 | COMMAND ${RSYNC} --delete -av --exclude '*~'
77 | ${PROJECT_SOURCE_DIR}/distrib-JavaScript/ ${DATAROOT}/)
78 | add_dependencies (stage-dist pack)
79 | add_custom_target (deploy-dist
80 | COMMAND ${RSYNC} --delete -av ${DATAROOT} ${FRSDEPLOY}/)
81 | endif ()
82 | endif ()
83 |
84 | if (IS_DIRECTORY ${PROJECT_SOURCE_DIR}/.git AND NOT WIN32)
85 | add_custom_target (checktrailingspace
86 | COMMAND git ls-files | xargs grep '[ \t]$$' || true
87 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
88 | COMMENT "Looking for trailing spaces")
89 | add_custom_target (checktabs
90 | COMMAND git ls-files | xargs grep '\t' || true
91 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
92 | COMMENT "Looking for tabs")
93 | add_custom_target (checkblanklines
94 | COMMAND git ls-files |
95 | while read f\; do tr 'X\\n' 'YX' < $$f |
96 | grep -E '\(^X|XXX|XX$$|[^X]$$\)' > /dev/null && echo $$f\; done || true
97 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
98 | COMMENT "Looking for extra blank lines")
99 |
100 | add_custom_target (sanitize)
101 | add_dependencies (sanitize checktrailingspace checktabs checkblanklines)
102 | endif ()
103 |
--------------------------------------------------------------------------------
/HOWTO-RELEASE.txt:
--------------------------------------------------------------------------------
1 | js packages needed (npm -i -g ... to install in ~/.local)
2 | jshint
3 | mocha
4 | minify
5 | jsdoc
6 |
7 | Update change log in doc/GeographicLib.md.
8 |
9 | Build with
10 |
11 | cmake -D BUILD_DOCUMENTATION=ON -D CMAKE_INSTALL_PREFIX=/tmp/js -B BUILD -S .
12 | cd BUILD
13 | make all # packages up node packages
14 | make lint
15 | make test
16 | make install
17 |
18 | This creates local modules geographiclib + geographiclib-dms in
19 | BUILD/node_modules
20 |
21 | and packages
22 | BUILD/geographiclib-${PROJECT_FULLVERSION}.tgz
23 | BUILD/geographiclib-dms-${PROJECT_FULLVERSION}.tgz
24 |
25 | installed modules in
26 | ${CMAKE_INSTALL_PREFIX}/${MODDIR}
27 |
28 | Options for make
29 | CMAKE_INSTALL_PREFIX head of install tree (default /usr/local)
30 | MODDIR where to install modules (default lib/node_modules)
31 | BUILD_DOCUMENTATION build documentation (default OFF)
32 |
33 | Other cmake targets
34 | sanitize (hygiene on source files)
35 | stage-doc, deploy-doc (move documentation to staging, sourceforge)
36 | stage-dist, deploy-dist (move distribution to staging, sourceforge)
37 | stage-scripts, deploy-scripts (move sample scripts to staging, sourceforge)
38 |
39 | Deployment to npmjs.org
40 | npm login
41 | npm publish BUILD/geographiclib-geodesic-${PROJECT_FULLVERSION}.tgz
42 | npm publish BUILD/geographiclib-dms-${PROJECT_FULLVERSION}.tgz
43 |
44 | N.B. node doesn't look in /usr/local/lib/node_modules by default, so
45 | if using globally installed package, invoke node with
46 |
47 | NODE_PATH=/usr/local/lib/node_modules node
48 |
49 | To debug path for node
50 | NODE_DEBUG=module node
51 | To set path for node
52 |
53 | To retrieve a tarball from npm use, e.g.,
54 | wget `npm view geographiclib dist.tarball`
55 |
56 | Delete alpha/beta tgz files from distrib-JavaScript
57 | Reset doc link for documentation.
58 |
59 | TODO: add version number + links to files in scripts
60 |
61 | Download scripts from
62 | https://cdn.jsdelivr.com/
63 |
64 |
70 |
71 | Get sha256 checksums with
72 | for f in node_modules/geographiclib-*/geographiclib-*.min.js; do
73 | echo $f
74 | openssl dgst -sha256 -binary $f | openssl base64 -A
75 | echo
76 | done
77 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT).
2 |
3 | Copyright (c) 2011-2022, Charles Karney
4 |
5 | Permission is hereby granted, free of charge, to any person
6 | obtaining a copy of this software and associated documentation
7 | files (the "Software"), to deal in the Software without
8 | restriction, including without limitation the rights to use, copy,
9 | modify, merge, publish, distribute, sublicense, and/or sell copies
10 | of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript implementation of the some routines in GeographicLib
2 |
3 | This repository hosts two JavaScript packages
4 | [geographiclib-geodesic](https://www.npmjs.com/package/geographiclib-geodesic)
5 | and
6 | [geographiclib-dms](https://www.npmjs.com/package/geographiclib-dms).
7 |
8 | Prior to version 2.0.0, these were combined in [node package
9 | geographiclib](https://www.npmjs.com/package/geographiclib). This
10 | package will be deprecated on 2023-05-01.
11 |
12 | Licensed under the MIT/X11 License; see
13 | [LICENSE.txt](https://geographiclib.sourceforge.io/LICENSE.txt).
14 |
15 | ## Documentation
16 |
17 | Full documentation is provided at
18 | [https://geographiclib.sourceforge.io/JavaScript/doc](
19 | https://geographiclib.sourceforge.io/JavaScript/doc/index.html).
20 |
--------------------------------------------------------------------------------
/distrib-JavaScript/00README.md:
--------------------------------------------------------------------------------
1 | # JavaScript implementation of the geodesic routines in GeographicLib
2 |
3 | node packages are `.tgz` files.
4 | ```bash
5 | npm install [PACKAGE-FILE]
6 | ```
7 |
8 | These packages are also hosted on [npmjs](https://www.npmjs.com) as
9 | [geographiclib-geodesic](https://www.npmjs.com/package/geographiclib-geodesic)
10 | and
11 | [geographiclib-dms](https://www.npmjs.com/package/geographiclib-dms).
12 |
13 | The algorithms are documented in
14 |
15 | * C. F. F. Karney,
16 | [Algorithms for geodesics](https://doi.org/10.1007/s00190-012-0578-z),
17 | J. Geodesy **87**(1), 43–55 (2013);
18 | [Addenda](https://geographiclib.sourceforge.io/geod-addenda.html).
19 |
20 | Other links:
21 |
22 | * Library documentation: https://geographiclib.sourceforge.io/JavaScript/doc
23 | * Git repository: https://github.com/geographiclib/geographiclib-js
24 | * GeographicLib: https://geographiclib.sourceforge.io
25 | * Author: Charles Karney,
26 |
--------------------------------------------------------------------------------
/dms/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | configure_file (package.json.in ${DESTDMS}/package.json @ONLY)
2 | configure_file (README.md ${DESTDMS} COPYONLY)
3 | configure_file (../LICENSE.txt ${DESTDMS} COPYONLY)
4 |
5 | configure_file (HEAD.js.in HEAD.js @ONLY)
6 | set (BUNDLEFILES ${CMAKE_CURRENT_BINARY_DIR}/HEAD.js)
7 |
8 | set (SOURCES DMS.js)
9 | foreach (_f ${SOURCES})
10 | configure_file (${_f} ${DESTDMS}/src/${_f} COPYONLY)
11 | list (APPEND BUNDLEFILES ${DESTDMS}/src/${_f})
12 | endforeach ()
13 |
14 | configure_file (TAIL.js . COPYONLY)
15 | list (APPEND BUNDLEFILES ${CMAKE_CURRENT_BINARY_DIR}/TAIL.js)
16 |
17 | configure_file (test/dmstest.js ${DESTDMS}/test/dmstest.js
18 | COPYONLY)
19 | configure_file (types/${DMS_PROJECT}.d.ts ${DESTDMS}/types/${DMS_PROJECT}.d.ts
20 | COPYONLY)
21 |
22 | add_custom_command (OUTPUT ${PACKAGEDDMSJS}
23 | COMMAND ${CMAKE_COMMAND} -E cat ${BUNDLEFILES} > ${PACKAGEDDMSJS}
24 | DEPENDS ${BUNDLEFILES}
25 | COMMENT "Making JS bundle for dms")
26 |
27 | add_custom_command (OUTPUT ${PACKAGEDDMSMINJS}
28 | # unicode character in minified file needs to be replaced
29 | COMMAND ${MINIFY} ${PACKAGEDDMSJS} > ${PACKAGEDDMSMINJS}
30 | DEPENDS ${PACKAGEDDMSJS}
31 | COMMENT "Making minified JS bundle for dms")
32 |
33 | add_custom_target (bundledms ALL
34 | DEPENDS ${PACKAGEDDMSJS} ${PACKAGEDDMSMINJS})
35 |
36 | install (DIRECTORY ${DESTDMS}
37 | DESTINATION ${MODDIR}
38 | FILES_MATCHING PATTERN "*.[jt]s" PATTERN "*.txt" PATTERN "*.json")
39 |
40 | if (MOCHA)
41 | add_test (NAME testdms
42 | COMMAND ${MOCHA}
43 | WORKING_DIRECTORY ${DESTDMS})
44 | endif ()
45 |
46 | # linting...
47 | if (LINT)
48 | add_custom_target (lintdms ${LINT} src WORKING_DIRECTORY ${DESTDMS}
49 | COMMENT "Linting dms with ${LINT}")
50 | endif ()
51 |
--------------------------------------------------------------------------------
/dms/DMS.js:
--------------------------------------------------------------------------------
1 | /*
2 | * DMS.js
3 | * Transcription of DMS.[ch]pp into JavaScript.
4 | *
5 | * See the documentation for the C++ class. The conversion is a literal
6 | * conversion from C++.
7 | *
8 | * Copyright (c) Charles Karney (2011-2020) and licensed
9 | * under the MIT/X11 License. For more information, see
10 | * https://geographiclib.sourceforge.io/
11 | */
12 |
13 | var DMS = {};
14 |
15 | (function(
16 | /**
17 | * @exports DMS
18 | * @description Decode/Encode angles expressed as degrees, minutes, and
19 | * seconds. This module defines several constants:
20 | * - hemisphere indicator (returned by
21 | * {@link module:DMS.Decode Decode}) and a formatting
22 | * indicator (used by
23 | * {@link module:DMS.Encode Encode})
24 | * - NONE = 0, no designator and format as plain angle;
25 | * - LATITUDE = 1, a N/S designator and format as latitude;
26 | * - LONGITUDE = 2, an E/W designator and format as longitude;
27 | * - AZIMUTH = 3, format as azimuth;
28 | * - the specification of the trailing component in
29 | * {@link module:DMS.Encode Encode}
30 | * - DEGREE = 0;
31 | * - MINUTE = 1;
32 | * - SECOND = 2.
33 | * @example
34 | * var DMS = require("geographiclib-dms"),
35 | * ang = DMS.Decode("127:54:3.123123W");
36 | * console.log("Azimuth " +
37 | * DMS.Encode(ang.val, DMS.MINUTE, 7, ang.ind) +
38 | * " = " + ang.val.toFixed(9));
39 | */
40 | d) {
41 | "use strict";
42 |
43 | var lookup, zerofill, internalDecode, numMatch,
44 | hemispheres_ = "SNWE",
45 | signs_ = "-+",
46 | digits_ = "0123456789",
47 | dmsindicators_ = "D'\":",
48 | // dmsindicatorsu_ = "\u00b0\u2032\u2033"; // Unicode variants
49 | dmsindicatorsu_ = "\u00b0'\"", // Use degree symbol
50 | // Minified js messes up degree symbol, but manually fix this
51 | // dmsindicatorsu_ = "d'\"", // Use d for degrees
52 | components_ = ["degrees", "minutes", "seconds"];
53 | lookup = function(s, c) {
54 | return s.indexOf(c.toUpperCase());
55 | };
56 | zerofill = function(s, n) {
57 | return "0000".substr(0, Math.max(0, Math.min(4, n-s.length))) + s;
58 | };
59 | d.NONE = 0;
60 | d.LATITUDE = 1;
61 | d.LONGITUDE = 2;
62 | d.AZIMUTH = 3;
63 | d.DEGREE = 0;
64 | d.MINUTE = 1;
65 | d.SECOND = 2;
66 |
67 | /**
68 | * @summary Decode a DMS string.
69 | * @param {string} dms the string.
70 | * @return {object} r where r.val is the decoded value (degrees) and r.ind
71 | * is a hemisphere designator, one of NONE, LATITUDE, LONGITUDE.
72 | * @throws an error if the string is illegal.
73 | *
74 | * @description Convert a DMS string into an angle.
75 | * Degrees, minutes, and seconds are indicated by the characters d, '
76 | * (single quote), " (double quote), and these components may only be
77 | * given in this order. Any (but not all) components may be omitted and
78 | * other symbols (e.g., the ° symbol for degrees and the unicode prime
79 | * and double prime symbols for minutes and seconds) may be substituted;
80 | * two single quotes can be used instead of ". The last component
81 | * indicator may be omitted and is assumed to be the next smallest unit
82 | * (thus 33d10 is interpreted as 33d10'). The final component may be a
83 | * decimal fraction but the non-final components must be integers. Instead
84 | * of using d, ', and " to indicate degrees, minutes, and seconds, :
85 | * (colon) may be used to separate these components (numbers must
86 | * appear before and after each colon); thus 50d30'10.3" may be
87 | * written as 50:30:10.3, 5.5' may be written 0:5.5, and so on. The
88 | * integer parts of the minutes and seconds components must be less
89 | * than 60. A single leading sign is permitted. A hemisphere designator
90 | * (N, E, W, S) may be added to the beginning or end of the string. The
91 | * result is multiplied by the implied sign of the hemisphere designator
92 | * (negative for S and W). In addition ind is set to DMS.LATITUDE if N
93 | * or S is present, to DMS.LONGITUDE if E or W is present, and to
94 | * DMS.NONE otherwise. Leading and trailing whitespace is removed from
95 | * the string before processing. This routine throws an error on a
96 | * malformed string. No check is performed on the range of the result.
97 | * Examples of legal and illegal strings are
98 | * - LEGAL (all the entries on each line are equivalent)
99 | * - -20.51125, 20d30'40.5"S, -20°30'40.5, -20d30.675,
100 | * N-20d30'40.5", -20:30:40.5
101 | * - 4d0'9, 4d9", 4d9'', 4:0:9, 004:00:09, 4.0025, 4.0025d, 4d0.15,
102 | * 04:.15
103 | * - 4:59.99999999999999, 4:60.0, 4:59:59.9999999999999, 4:59:60.0, 5
104 | * - ILLEGAL (the exception thrown explains the problem)
105 | * - 4d5"4', 4::5, 4:5:, :4:5, 4d4.5'4", -N20.5, 1.8e2d, 4:60,
106 | * 4:59:60
107 | *
108 | * The decoding operation can also perform addition and subtraction
109 | * operations. If the string includes internal signs (i.e., not at
110 | * the beginning nor immediately after an initial hemisphere designator),
111 | * then the string is split immediately before such signs and each piece is
112 | * decoded according to the above rules and the results added; thus
113 | * S3-2.5+4.1N
is parsed as the sum of S3
,
114 | * -2.5
, +4.1N
. Any piece can include a
115 | * hemisphere designator; however, if multiple designators are given, they
116 | * must compatible; e.g., you cannot mix N and E. In addition, the
117 | * designator can appear at the beginning or end of the first piece, but
118 | * must be at the end of all subsequent pieces (a hemisphere designator is
119 | * not allowed after the initial sign). Examples of legal and illegal
120 | * combinations are
121 | * - LEGAL (these are all equivalent)
122 | * - -070:00:45, 70:01:15W+0:0.5, 70:01:15W-0:0:30W, W70:01:15+0:0:30E
123 | * - ILLEGAL (the exception thrown explains the problem)
124 | * - 70:01:15W+0:0:15N, W70:01:15+W0:0:15
125 | *
126 | * WARNING The "exponential" notation is not recognized. Thus
127 | * 7.0E1
is illegal, while 7.0E+1
is parsed as
128 | * (7.0E) + (+1)
, yielding the same result as
129 | * 8.0E
.
130 | */
131 | d.Decode = function(dms) {
132 | var dmsa = dms, end,
133 | // v = -0.0, so "-0" returns -0.0
134 | v = -0, i = 0, mi, pi, vals,
135 | ind1 = d.NONE, ind2, p, pa, pb;
136 | dmsa = dmsa
137 | .replace(/\u00b0/g, 'd' ) // U+00b0 degree symbol
138 | .replace(/\u00ba/g, 'd' ) // U+00ba alt symbol
139 | .replace(/\u2070/g, 'd' ) // U+2070 sup zero
140 | .replace(/\u02da/g, 'd' ) // U+02da ring above
141 | .replace(/\u2218/g, 'd' ) // U+2218 compose function
142 | .replace(/\*/g , 'd' ) // GRiD symbol for degree
143 |
144 | .replace(/`/g , 'd' ) // grave accent
145 | .replace(/\u2032/g, '\'') // U+2032 prime
146 | .replace(/\u2035/g, '\'') // U+2035 back prime
147 | .replace(/\u00b4/g, '\'') // U+00b4 acute accent
148 | .replace(/\u2018/g, '\'') // U+2018 left single quote
149 | .replace(/\u2019/g, '\'') // U+2019 right single quote
150 | .replace(/\u201b/g, '\'') // U+201b reversed-9 single quote
151 | .replace(/\u02b9/g, '\'') // U+02b9 modifier letter prime
152 | .replace(/\u02ca/g, '\'') // U+02ca modifier letter acute accent
153 | .replace(/\u02cb/g, '\'') // U+02cb modifier letter grave accent
154 |
155 | .replace(/\u2033/g, '"' ) // U+2033 double prime
156 | .replace(/\u2036/g, '"' ) // U+2036 reversed double prime
157 | .replace(/\u02dd/g, '"' ) // U+02dd double acute accent
158 | .replace(/\u201c/g, '"' ) // U+201d left double quote
159 | .replace(/\u201d/g, '"' ) // U+201d right double quote
160 | .replace(/\u201f/g, '"' ) // U+201f reversed-9 double quote
161 | .replace(/\u02ba/g, '"' ) // U+02ba modifier letter double prime
162 |
163 | .replace(/\u2795/g, '+' ) // U+2795 heavy plus
164 | .replace(/\u2064/g, '+' ) // U+2064 invisible plus
165 |
166 | .replace(/\u2010/g, '-' ) // U+2010 dash
167 | .replace(/\u2011/g, '-' ) // U+2011 non-breaking hyphen
168 | .replace(/\u2013/g, '-' ) // U+2013 en dash
169 | .replace(/\u2014/g, '-' ) // U+2014 em dash
170 | .replace(/\u2212/g, '-' ) // U+2212 minus sign
171 | .replace(/\u2796/g, '-' ) // U+2796 heavy minus
172 |
173 | .replace(/\u00a0/g, '' ) // U+00a0 non-breaking space
174 | .replace(/\u2007/g, '' ) // U+2007 figure space
175 | .replace(/\u2009/g, '' ) // U+2009 thin space
176 | .replace(/\u200a/g, '' ) // U+200a hair space
177 | .replace(/\u200b/g, '' ) // U+200b invisible space
178 | .replace(/\u202f/g, '' ) // U+202f narrow space
179 | .replace(/\u2063/g, '' ) // U+2063 invisible separator
180 |
181 | .replace(/''/g, '"' ) // '' -> "
182 |
183 | .trim();
184 |
185 | end = dmsa.length;
186 | // p is pointer to the next piece that needs decoding
187 | for (p = 0; p < end; p = pb, ++i) {
188 | pa = p;
189 | // Skip over initial hemisphere letter (for i == 0)
190 | if (i === 0 && lookup(hemispheres_, dmsa.charAt(pa)) >= 0)
191 | ++pa;
192 | // Skip over initial sign (checking for it if i == 0)
193 | if (i > 0 || (pa < end && lookup(signs_, dmsa.charAt(pa)) >= 0))
194 | ++pa;
195 | // Find next sign
196 | mi = dmsa.substr(pa, end - pa).indexOf('-');
197 | pi = dmsa.substr(pa, end - pa).indexOf('+');
198 | if (mi < 0) mi = end; else mi += pa;
199 | if (pi < 0) pi = end; else pi += pa;
200 | pb = Math.min(mi, pi);
201 | vals = internalDecode(dmsa.substr(p, pb - p));
202 | v += vals.val; ind2 = vals.ind;
203 | if (ind1 === d.NONE)
204 | ind1 = ind2;
205 | else if (!(ind2 === d.NONE || ind1 === ind2))
206 | throw new Error("Incompatible hemisphere specifies in " +
207 | dmsa.substr(0, pb));
208 | }
209 | if (i === 0)
210 | throw new Error("Empty or incomplete DMS string " + dmsa);
211 | return {val: v, ind: ind1};
212 | };
213 |
214 | internalDecode = function(dmsa) {
215 | var vals = {}, errormsg = "",
216 | sign, beg, end, ind1, k,
217 | ipieces, fpieces, npiece,
218 | icurrent, fcurrent, ncurrent, p,
219 | pointseen,
220 | digcount, intcount,
221 | x;
222 | do { // Executed once (provides the ability to break)
223 | sign = 1;
224 | beg = 0; end = dmsa.length;
225 | ind1 = d.NONE;
226 | k = -1;
227 | if (end > beg && (k = lookup(hemispheres_, dmsa.charAt(beg))) >= 0) {
228 | ind1 = (k & 2) ? d.LONGITUDE : d.LATITUDE;
229 | sign = (k & 1) ? 1 : -1;
230 | ++beg;
231 | }
232 | if (end > beg &&
233 | (k = lookup(hemispheres_, dmsa.charAt(end-1))) >= 0) {
234 | if (k >= 0) {
235 | if (ind1 !== d.NONE) {
236 | if (dmsa.charAt(beg - 1).toUpperCase() ===
237 | dmsa.charAt(end - 1).toUpperCase())
238 | errormsg = "Repeated hemisphere indicators " +
239 | dmsa.charAt(beg - 1) + " in " +
240 | dmsa.substr(beg - 1, end - beg + 1);
241 | else
242 | errormsg = "Contradictory hemisphere indicators " +
243 | dmsa.charAt(beg - 1) + " and " + dmsa.charAt(end - 1) + " in " +
244 | dmsa.substr(beg - 1, end - beg + 1);
245 | break;
246 | }
247 | ind1 = (k & 2) ? d.LONGITUDE : d.LATITUDE;
248 | sign = (k & 1) ? 1 : -1;
249 | --end;
250 | }
251 | }
252 | if (end > beg && (k = lookup(signs_, dmsa.charAt(beg))) >= 0) {
253 | if (k >= 0) {
254 | sign *= k ? 1 : -1;
255 | ++beg;
256 | }
257 | }
258 | if (end === beg) {
259 | errormsg = "Empty or incomplete DMS string " + dmsa;
260 | break;
261 | }
262 | ipieces = [0, 0, 0];
263 | fpieces = [0, 0, 0];
264 | npiece = 0;
265 | icurrent = 0;
266 | fcurrent = 0;
267 | ncurrent = 0;
268 | p = beg;
269 | pointseen = false;
270 | digcount = 0;
271 | intcount = 0;
272 | while (p < end) {
273 | x = dmsa.charAt(p++);
274 | if ((k = lookup(digits_, x)) >= 0) {
275 | ++ncurrent;
276 | if (digcount > 0) {
277 | ++digcount; // Count of decimal digits
278 | } else {
279 | icurrent = 10 * icurrent + k;
280 | ++intcount;
281 | }
282 | } else if (x === '.') {
283 | if (pointseen) {
284 | errormsg = "Multiple decimal points in " +
285 | dmsa.substr(beg, end - beg);
286 | break;
287 | }
288 | pointseen = true;
289 | digcount = 1;
290 | } else if ((k = lookup(dmsindicators_, x)) >= 0) {
291 | if (k >= 3) {
292 | if (p === end) {
293 | errormsg = "Illegal for colon to appear at the end of " +
294 | dmsa.substr(beg, end - beg);
295 | break;
296 | }
297 | k = npiece;
298 | }
299 | if (k === npiece - 1) {
300 | errormsg = "Repeated " + components_[k] +
301 | " component in " + dmsa.substr(beg, end - beg);
302 | break;
303 | } else if (k < npiece) {
304 | errormsg = components_[k] + " component follows " +
305 | components_[npiece - 1] + " component in " +
306 | dmsa.substr(beg, end - beg);
307 | break;
308 | }
309 | if (ncurrent === 0) {
310 | errormsg = "Missing numbers in " + components_[k] +
311 | " component of " + dmsa.substr(beg, end - beg);
312 | break;
313 | }
314 | if (digcount > 0) {
315 | fcurrent = parseFloat(dmsa.substr(p - intcount - digcount - 1,
316 | intcount + digcount));
317 | icurrent = 0;
318 | }
319 | ipieces[k] = icurrent;
320 | fpieces[k] = icurrent + fcurrent;
321 | if (p < end) {
322 | npiece = k + 1;
323 | icurrent = fcurrent = 0;
324 | ncurrent = digcount = intcount = 0;
325 | }
326 | } else if (lookup(signs_, x) >= 0) {
327 | errormsg = "Internal sign in DMS string " +
328 | dmsa.substr(beg, end - beg);
329 | break;
330 | } else {
331 | errormsg = "Illegal character " + x + " in DMS string " +
332 | dmsa.substr(beg, end - beg);
333 | break;
334 | }
335 | }
336 | if (errormsg.length)
337 | break;
338 | if (lookup(dmsindicators_, dmsa.charAt(p - 1)) < 0) {
339 | if (npiece >= 3) {
340 | errormsg = "Extra text following seconds in DMS string " +
341 | dmsa.substr(beg, end - beg);
342 | break;
343 | }
344 | if (ncurrent === 0) {
345 | errormsg = "Missing numbers in trailing component of " +
346 | dmsa.substr(beg, end - beg);
347 | break;
348 | }
349 | if (digcount > 0) {
350 | fcurrent = parseFloat(dmsa.substr(p - intcount - digcount,
351 | intcount + digcount));
352 | icurrent = 0;
353 | }
354 | ipieces[npiece] = icurrent;
355 | fpieces[npiece] = icurrent + fcurrent;
356 | }
357 | if (pointseen && digcount === 0) {
358 | errormsg = "Decimal point in non-terminal component of " +
359 | dmsa.substr(beg, end - beg);
360 | break;
361 | }
362 | // Note that we accept 59.999999... even though it rounds to 60.
363 | if (ipieces[1] >= 60 || fpieces[1] > 60) {
364 | errormsg = "Minutes " + fpieces[1] + " not in range [0,60)";
365 | break;
366 | }
367 | if (ipieces[2] >= 60 || fpieces[2] > 60) {
368 | errormsg = "Seconds " + fpieces[2] + " not in range [0,60)";
369 | break;
370 | }
371 | vals.ind = ind1;
372 | // Assume check on range of result is made by calling routine (which
373 | // might be able to offer a better diagnostic).
374 | vals.val = sign *
375 | ( fpieces[2] ? (60*(60*fpieces[0] + fpieces[1]) + fpieces[2]) / 3600 :
376 | ( fpieces[1] ? (60*fpieces[0] + fpieces[1]) / 60 : fpieces[0] ) );
377 | return vals;
378 | } while (false);
379 | vals.val = numMatch(dmsa);
380 | if (vals.val === 0)
381 | throw new Error(errormsg);
382 | else
383 | vals.ind = d.NONE;
384 | return vals;
385 | };
386 |
387 | numMatch = function(s) {
388 | var t, sign, p0, p1;
389 | if (s.length < 3)
390 | return 0;
391 | t = s.toUpperCase().replace(/0+$/, "");
392 | sign = t.charAt(0) === '-' ? -1 : 1;
393 | p0 = t.charAt(0) === '-' || t.charAt(0) === '+' ? 1 : 0;
394 | p1 = t.length - 1;
395 | if (p1 + 1 < p0 + 3)
396 | return 0;
397 | // Strip off sign and trailing 0s
398 | t = t.substr(p0, p1 + 1 - p0); // Length at least 3
399 | if (t === "NAN" || t === "1.#QNAN" || t === "1.#SNAN" || t === "1.#IND" ||
400 | t === "1.#R")
401 | return Number.NaN;
402 | else if (t === "INF" || t === "1.#INF" || t === "INFINITY")
403 | return sign * Number.POSITIVE_INFINITY;
404 | return 0;
405 | };
406 |
407 | /**
408 | * @summary Decode two DMS strings interpreting them as a latitude/longitude
409 | * pair.
410 | * @param {string} stra the first string.
411 | * @param {string} strb the first string.
412 | * @param {bool} [longfirst = false] if true assume then longitude is given
413 | * first (in the absence of any hemisphere indicators).
414 | * @return {object} r where r.lat is the decoded latitude and r.lon is the
415 | * decoded longitude (both in degrees).
416 | * @throws an error if the strings are illegal.
417 | */
418 | d.DecodeLatLon = function(stra, strb, longfirst) {
419 | var vals = {},
420 | valsa = d.Decode(stra),
421 | valsb = d.Decode(strb),
422 | a = valsa.val, ia = valsa.ind,
423 | b = valsb.val, ib = valsb.ind,
424 | lat, lon;
425 | if (!longfirst) longfirst = false;
426 | if (ia === d.NONE && ib === d.NONE) {
427 | // Default to lat, long unless longfirst
428 | ia = longfirst ? d.LONGITUDE : d.LATITUDE;
429 | ib = longfirst ? d.LATITUDE : d.LONGITUDE;
430 | } else if (ia === d.NONE)
431 | ia = d.LATITUDE + d.LONGITUDE - ib;
432 | else if (ib === d.NONE)
433 | ib = d.LATITUDE + d.LONGITUDE - ia;
434 | if (ia === ib)
435 | throw new Error("Both " + stra + " and " + strb + " interpreted as " +
436 | (ia === d.LATITUDE ? "latitudes" : "longitudes"));
437 | lat = ia === d.LATITUDE ? a : b;
438 | lon = ia === d.LATITUDE ? b : a;
439 | if (Math.abs(lat) > 90)
440 | throw new Error("Latitude " + lat + " not in [-90,90]");
441 | vals.lat = lat;
442 | vals.lon = lon;
443 | return vals;
444 | };
445 |
446 | /**
447 | * @summary Decode a DMS string interpreting it as an arc length.
448 | * @param {string} angstr the string (this must not include a hemisphere
449 | * indicator).
450 | * @return {number} the arc length (degrees).
451 | * @throws an error if the string is illegal.
452 | */
453 | d.DecodeAngle = function(angstr) {
454 | var vals = d.Decode(angstr),
455 | ang = vals.val, ind = vals.ind;
456 | if (ind !== d.NONE)
457 | throw new Error("Arc angle " + angstr +
458 | " includes a hemisphere N/E/W/S");
459 | return ang;
460 | };
461 |
462 | /**
463 | * @summary Decode a DMS string interpreting it as an azimuth.
464 | * @param {string} azistr the string (this may include an E/W hemisphere
465 | * indicator).
466 | * @return {number} the azimuth (degrees).
467 | * @throws an error if the string is illegal.
468 | */
469 | d.DecodeAzimuth = function(azistr) {
470 | var vals = d.Decode(azistr),
471 | azi = vals.val, ind = vals.ind;
472 | if (ind === d.LATITUDE)
473 | throw new Error("Azimuth " + azistr + " has a latitude hemisphere N/S");
474 | return azi;
475 | };
476 |
477 | /**
478 | * @summary Convert angle (in degrees) into a DMS string (using °, ',
479 | * and ").
480 | * @param {number} angle input angle (degrees).
481 | * @param {number} trailing one of DEGREE, MINUTE, or SECOND to indicate
482 | * the trailing component of the string (this component is given as a
483 | * decimal number if necessary).
484 | * @param {number} prec the number of digits after the decimal point for
485 | * the trailing component.
486 | * @param {number} [ind = NONE] a formatting indicator, one of NONE,
487 | * LATITUDE, LONGITUDE, AZIMUTH.
488 | * @param {char} [dmssep = NULL] if non-null, use as the DMS separator
489 | * character.
490 | * @return {string} the resulting string formatted as follows:
491 | * * NONE, signed result no leading zeros on degrees except in the units
492 | * place, e.g., -8°03'.
493 | * * LATITUDE, trailing N or S hemisphere designator, no sign, pad
494 | * degrees to 2 digits, e.g., 08°03'S.
495 | * * LONGITUDE, trailing E or W hemisphere designator, no sign, pad
496 | * degrees to 3 digits, e.g., 008°03'W.
497 | * * AZIMUTH, convert to the range [0, 360°), no sign, pad degrees to
498 | * 3 digits, e.g., 351°57'.
499 | *
500 | * WARNING Because of implementation of JavaScript's toFixed function,
501 | * this routine rounds ties away from zero. This is different from the C++
502 | * version of GeographicLib which implements the "round ties to even" rule.
503 | *
504 | * WARNING Angles whose magnitude is equal to or greater than
505 | * 1021 are printed as a plain number in exponential notation,
506 | * e.g., "1e21".
507 | */
508 | d.Encode = function(angle, trailing, prec, ind, dmssep) {
509 | // Assume check on range of input angle has been made by calling
510 | // routine (which might be able to offer a better diagnostic).
511 | var scale = 1, i, sign,
512 | idegree, fdegree, degree, minute, second, s, usesep, p;
513 | if (!ind) ind = d.NONE;
514 | if (!dmssep) dmssep = '\0';
515 | usesep = dmssep !== '\0';
516 | if (!isFinite(angle))
517 | return angle < 0 ? "-inf" :
518 | (angle > 0 ? "inf" : "nan");
519 | if (Math.abs(angle) >= 1e21)
520 | // toFixed only works for numbers less that 1e21.
521 | return angle.toString().replace(/e\+/, 'e'); // remove "+" from exponent
522 |
523 | // 15 - 2 * trailing = ceiling(log10(2^53/90/60^trailing)).
524 | // This suffices to give full real precision for numbers in [-90,90]
525 | prec = Math.min(15 - 2 * trailing, prec);
526 | for (i = 0; i < trailing; ++i)
527 | scale *= 60;
528 | if (ind === d.AZIMUTH) {
529 | angle %= 360;
530 | // Only angles strictly less than 0 can become 360; since +/-180 are
531 | // folded together, we convert -0 to +0 (instead of 360).
532 | if (angle < 0)
533 | angle += 360;
534 | else
535 | angle = 0 + angle;
536 | }
537 | sign = (angle < 0 || angle === 0 && 1/angle < 0) ? -1 : 1;
538 | angle *= sign;
539 |
540 | // Break off integer part to preserve precision and avoid overflow in
541 | // manipulation of fractional part for MINUTE and SECOND
542 | idegree = trailing === d.DEGREE ? 0 : Math.floor(angle);
543 | fdegree = (angle - idegree) * scale;
544 | s = fdegree.toFixed(prec);
545 | switch (trailing) {
546 | case d.DEGREE:
547 | degree = s;
548 | break;
549 | default: // case MINUTE: case SECOND:
550 | p = s.indexOf('.');
551 | if (p < 0) {
552 | i = parseInt(s);
553 | s = "";
554 | } else if (p === 0) {
555 | i = 0;
556 | } else {
557 | i = parseInt(s.substr(0, p));
558 | s = s.substr(p);
559 | }
560 | // Now i in [0,60] or [0,3600] for MINUTE/DEGREE
561 | switch (trailing) {
562 | case d.MINUTE:
563 | minute = (i % 60).toString() + s; i = Math.trunc(i / 60);
564 | degree = (i + idegree).toFixed(0); // no overflow since i in [0,1]
565 | break;
566 | default: // case SECOND:
567 | second = (i % 60).toString() + s; i = Math.trunc(i / 60);
568 | minute = (i % 60).toString() ; i = Math.trunc(i / 60);
569 | degree = (i + idegree).toFixed(0); // no overflow since i in [0,1]
570 | break;
571 | }
572 | break;
573 | }
574 | // No glue together degree+minute+second with
575 | // sign + zero-fill + delimiters + hemisphere
576 | s = "";
577 | if (ind === d.NONE && sign < 0)
578 | s += '-';
579 | if (prec) ++prec; // Extra width for decimal point
580 | switch (trailing) {
581 | case d.DEGREE:
582 | s += zerofill(degree, ind === d.NONE ? 0 : 1 + Math.min(ind, 2) + prec) +
583 | (usesep ? '' : dmsindicatorsu_.charAt(0));
584 | break;
585 | case d.MINUTE:
586 | s += zerofill(degree, ind === d.NONE ? 0 : 1 + Math.min(ind, 2)) +
587 | (usesep ? dmssep : dmsindicatorsu_.charAt(0)) +
588 | zerofill(minute, 2 + prec) +
589 | (usesep ? '' : dmsindicatorsu_.charAt(1));
590 | break;
591 | default: // case SECOND:
592 | s += zerofill(degree, ind === d.NONE ? 0 : 1 + Math.min(ind, 2)) +
593 | (usesep ? dmssep : dmsindicatorsu_.charAt(0)) +
594 | zerofill(minute, 2) +
595 | (usesep ? dmssep : dmsindicatorsu_.charAt(1)) +
596 | zerofill(second, 2 + prec) +
597 | (usesep ? '' : dmsindicatorsu_.charAt(2));
598 | break;
599 | }
600 | if (ind !== d.NONE && ind !== d.AZIMUTH)
601 | s += hemispheres_.charAt((ind === d.LATITUDE ? 0 : 2) +
602 | (sign < 0 ? 0 : 1));
603 | return s;
604 | };
605 | })(DMS);
606 |
--------------------------------------------------------------------------------
/dms/HEAD.js.in:
--------------------------------------------------------------------------------
1 | /*
2 | * DMS routines from GeographicLib translated to JavaScript. See
3 | * https://geographiclib.sourceforge.io/JavaScript/doc
4 | *
5 | * This file is the concatenation and compression of the JavaScript files in
6 | * src/dms in the source tree for geographiclib-js.
7 | *
8 | * Copyright (c) Charles Karney (2011-2022) and licensed
9 | * under the MIT/X11 License. For more information, see
10 | * https://geographiclib.sourceforge.io/
11 | *
12 | * Version: @PROJECT_FULLVERSION@
13 | * Date: @RELEASE_DATE@
14 | */
15 |
16 | (function(cb) {
17 |
--------------------------------------------------------------------------------
/dms/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript implementation of the DMS routines in GeographicLib
2 |
3 | This package is a JavaScript implementation of the DMS (degree,
4 | minute, second) handling routines from
5 | [GeographicLib](https://geographiclib.sourceforge.io).
6 |
7 | Prior to version 2.0.0, this was a component of the [node package
8 | geographiclib](https://www.npmjs.com/package/geographiclib). As of
9 | version 2.0.0, that package was split into the packages
10 | [geographiclib-geodesic](https://www.npmjs.com/package/geographiclib-geodesic)
11 | and
12 | [geographiclib-dms](https://www.npmjs.com/package/geographiclib-dms)
13 | (this package).
14 | [geographiclib](https://www.npmjs.com/package/geographiclib) will be
15 | deprecated on 2023-05-01.
16 |
17 | Licensed under the MIT/X11 License; see
18 | [LICENSE.txt](https://geographiclib.sourceforge.io/LICENSE.txt).
19 |
20 | ## Installation
21 |
22 | ```bash
23 | $ npm install geographiclib-dms
24 | ```
25 |
26 | ## Usage
27 |
28 | In [node](https://nodejs.org), do
29 | ```javascript
30 | var DMS = require("geographiclib-dms");
31 | ```
32 |
33 | ## Documentation
34 |
35 | Full documentation is provided at
36 | [https://geographiclib.sourceforge.io/JavaScript/doc](
37 | https://geographiclib.sourceforge.io/JavaScript/doc/index.html).
38 |
39 | ## Examples
40 |
41 | ```javascript
42 | var DMS = require("geographiclib-dms"),
43 | ang = DMS.Decode("127:54:3.123123W");
44 | console.log("Azimuth " +
45 | DMS.Encode(ang.val, DMS.MINUTE, 7, ang.ind) +
46 | " = " + ang.val.toFixed(9));
47 | // This prints "Azimuth 127°54.0520521'W = -127.900867534"
48 | ```
49 |
50 | ## Authors
51 |
52 | * algorithms + js code: Charles Karney (karney@alum.mit.edu)
53 | * node.js port: Yurij Mikhalevich (yurij@mikhalevi.ch)
54 |
--------------------------------------------------------------------------------
/dms/TAIL.js:
--------------------------------------------------------------------------------
1 | cb(DMS);
2 |
3 | })(function(dms) {
4 | if (typeof module === 'object' && module.exports) {
5 | /******** support loading with node's require ********/
6 | module.exports = dms;
7 | } else if (typeof define === 'function' && define.amd) {
8 | /******** support loading with AMD ********/
9 | define('geographiclib-dms', [], function() { return dms; });
10 | } else {
11 | /******** otherwise just pollute our global namespace ********/
12 | window.DMS = dms;
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/dms/package.json.in:
--------------------------------------------------------------------------------
1 | {
2 | "name": "geographiclib-dms",
3 | "version": "@PROJECT_FULLVERSION@",
4 | "description": "JavaScript implementation of DMS routines in GeographicLib",
5 | "main": "geographiclib-dms.min.js",
6 | "types": "types/geographiclib-dms.d.ts",
7 | "scripts": {
8 | "test": "mocha"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/geographiclib/geographiclib-js.git"
13 | },
14 | "url": "https://geographiclib.sourceforge.net",
15 | "keywords": [
16 | "DMS",
17 | "degrees-minutes-seconds"
18 | ],
19 | "devDependencies": {
20 | "mocha": "^9.1.5",
21 | "minify": "^7.0.2"
22 | },
23 | "author": "Charles Karney ",
24 | "contributors": [
25 | "Yurij Mikhalevich "
26 | ],
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/geographiclib/geographiclib-js/issues",
30 | "email": "karney@alum.mit.edu"
31 | },
32 | "homepage": "https://github.com/geographiclib/geographiclib-js#readme",
33 | "directories": {
34 | "test": "test"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/dms/test/dmstest.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var assert = require("assert"),
4 | d = require("../geographiclib-dms");
5 |
6 | describe("DMS", function() {
7 | describe("DMSTest", function () {
8 |
9 | it("check decode", function () {
10 | assert.deepEqual(d.Decode("E7:33:36"), d.Decode("-7.56W"));
11 | });
12 |
13 | it("check encode", function () {
14 | var t;
15 | assert.strictEqual(d.Encode(-7.56, d.DEGREE, 2), "-7.56°");
16 | assert.strictEqual(d.Encode(-7.56, d.MINUTE, 1), "-7°33.6'");
17 | assert.strictEqual(d.Encode(-7.56, d.SECOND, 0), "-7°33'36\"");
18 | assert.strictEqual(d.Encode(-7.56, d.DEGREE, 2, d.NONE, ':'), "-7.56");
19 | assert.strictEqual(d.Encode(-7.56, d.MINUTE, 1, d.NONE, ':'), "-7:33.6");
20 | assert.strictEqual(d.Encode(-7.56, d.SECOND, 0, d.NONE, ':'), "-7:33:36");
21 |
22 | assert.strictEqual(d.Encode(-7.56, d.DEGREE, 2, d.LATITUDE),
23 | "07.56°S");
24 | assert.strictEqual(d.Encode(-7.56, d.MINUTE, 1, d.LATITUDE),
25 | "07°33.6'S");
26 | assert.strictEqual(d.Encode(-7.56, d.SECOND, 0, d.LATITUDE),
27 | "07°33'36\"S");
28 | assert.strictEqual(d.Encode(-7.56, d.DEGREE, 2, d.LATITUDE, ':'),
29 | "07.56S");
30 | assert.strictEqual(d.Encode(-7.56, d.MINUTE, 1, d.LATITUDE, ':'),
31 | "07:33.6S");
32 | assert.strictEqual(d.Encode(-7.56, d.SECOND, 0, d.LATITUDE, ':'),
33 | "07:33:36S");
34 | // zero fill checks
35 | t = -(1 + 2/60 + 2.99/3600);
36 | assert.strictEqual(d.Encode( t,d.DEGREE,0,d.NONE ), "-1°" );
37 | assert.strictEqual(d.Encode( t,d.DEGREE,0,d.LATITUDE ), "01°S" );
38 | assert.strictEqual(d.Encode( t,d.DEGREE,0,d.LONGITUDE),"001°W" );
39 | assert.strictEqual(d.Encode(-t,d.DEGREE,0,d.AZIMUTH ),"001°" );
40 | assert.strictEqual(d.Encode( t,d.DEGREE,1,d.NONE ), "-1.0°" );
41 | assert.strictEqual(d.Encode( t,d.DEGREE,1,d.LATITUDE ), "01.0°S" );
42 | assert.strictEqual(d.Encode( t,d.DEGREE,1,d.LONGITUDE),"001.0°W" );
43 | assert.strictEqual(d.Encode(-t,d.DEGREE,1,d.AZIMUTH ),"001.0°" );
44 | assert.strictEqual(d.Encode( t,d.MINUTE,0,d.NONE ), "-1°02'" );
45 | assert.strictEqual(d.Encode( t,d.MINUTE,0,d.LATITUDE ), "01°02'S" );
46 | assert.strictEqual(d.Encode( t,d.MINUTE,0,d.LONGITUDE),"001°02'W" );
47 | assert.strictEqual(d.Encode(-t,d.MINUTE,0,d.AZIMUTH ),"001°02'" );
48 | assert.strictEqual(d.Encode( t,d.MINUTE,1,d.NONE ), "-1°02.0'" );
49 | assert.strictEqual(d.Encode( t,d.MINUTE,1,d.LATITUDE ), "01°02.0'S" );
50 | assert.strictEqual(d.Encode( t,d.MINUTE,1,d.LONGITUDE),"001°02.0'W" );
51 | assert.strictEqual(d.Encode(-t,d.MINUTE,1,d.AZIMUTH ),"001°02.0'" );
52 | assert.strictEqual(d.Encode( t,d.SECOND,0,d.NONE ), "-1°02'03\"" );
53 | assert.strictEqual(d.Encode( t,d.SECOND,0,d.LATITUDE ), "01°02'03\"S" );
54 | assert.strictEqual(d.Encode( t,d.SECOND,0,d.LONGITUDE),"001°02'03\"W" );
55 | assert.strictEqual(d.Encode(-t,d.SECOND,0,d.AZIMUTH ),"001°02'03\"" );
56 | assert.strictEqual(d.Encode( t,d.SECOND,1,d.NONE ), "-1°02'03.0\"" );
57 | assert.strictEqual(d.Encode( t,d.SECOND,1,d.LATITUDE ), "01°02'03.0\"S");
58 | assert.strictEqual(d.Encode( t,d.SECOND,1,d.LONGITUDE),"001°02'03.0\"W");
59 | assert.strictEqual(d.Encode(-t,d.SECOND,1,d.AZIMUTH ),"001°02'03.0\"" );
60 | });
61 |
62 | it("check decode special", function () {
63 | var nan = NaN, inf = Infinity;
64 | assert.strictEqual(d.Decode(" +0 ").val, +0);
65 | assert.strictEqual(d.Decode("-0 ").val, -0);
66 | assert.strictEqual(d.Decode(" nan").val, nan);
67 | assert.strictEqual(d.Decode("+inf").val, +inf);
68 | assert.strictEqual(d.Decode(" inf").val, +inf);
69 | assert.strictEqual(d.Decode("-inf").val, -inf);
70 | assert.strictEqual(d.Decode(" +0N").val, +0);
71 | assert.strictEqual(d.Decode("-0N ").val, -0);
72 | assert.strictEqual(d.Decode("+0S ").val, -0);
73 | assert.strictEqual(d.Decode(" -0S").val, +0);
74 | });
75 |
76 | it("check encode rounding", function () {
77 | var nan = NaN, inf = Infinity;
78 | // JavaScript rounds ties away from zero...
79 | // Round to even results given in trailing comments
80 | assert.strictEqual(d.Encode( nan , d.DEGREE, 0), "nan" );
81 | assert.strictEqual(d.Encode(-inf , d.DEGREE, 0), "-inf" );
82 | assert.strictEqual(d.Encode(-3.5 , d.DEGREE, 0), "-4°" );
83 | assert.strictEqual(d.Encode(-2.5 , d.DEGREE, 0), "-3°" ); // -2
84 | assert.strictEqual(d.Encode(-1.5 , d.DEGREE, 0), "-2°" );
85 | assert.strictEqual(d.Encode(-0.5 , d.DEGREE, 0), "-1°" ); // -0
86 | assert.strictEqual(d.Encode(-0 , d.DEGREE, 0), "-0°" );
87 | assert.strictEqual(d.Encode(+0 , d.DEGREE, 0), "0°" );
88 | assert.strictEqual(d.Encode(+0.5 , d.DEGREE, 0), "1°" ); // 0
89 | assert.strictEqual(d.Encode(+1.5 , d.DEGREE, 0), "2°" );
90 | assert.strictEqual(d.Encode(+2.5 , d.DEGREE, 0), "3°" ); // 2
91 | assert.strictEqual(d.Encode(+3.5 , d.DEGREE, 0), "4°" );
92 | assert.strictEqual(d.Encode(+inf , d.DEGREE, 0), "inf" );
93 | assert.strictEqual(d.Encode(-1.75, d.DEGREE, 1), "-1.8°");
94 | assert.strictEqual(d.Encode(-1.25, d.DEGREE, 1), "-1.3°"); // -1.2
95 | assert.strictEqual(d.Encode(-0.75, d.DEGREE, 1), "-0.8°");
96 | assert.strictEqual(d.Encode(-0.25, d.DEGREE, 1), "-0.3°"); // 0.2
97 | assert.strictEqual(d.Encode(-0 , d.DEGREE, 1), "-0.0°");
98 | assert.strictEqual(d.Encode(+0 , d.DEGREE, 1), "0.0°");
99 | assert.strictEqual(d.Encode(+0.25, d.DEGREE, 1), "0.3°"); // 0.2
100 | assert.strictEqual(d.Encode(+0.75, d.DEGREE, 1), "0.8°");
101 | assert.strictEqual(d.Encode(+1.25, d.DEGREE, 1), "1.3°"); // 1.2
102 | assert.strictEqual(d.Encode(+1.75, d.DEGREE, 1), "1.8°");
103 | assert.strictEqual(d.Encode( 1e20, d.DEGREE, 0),
104 | "100000000000000000000°");
105 | assert.strictEqual(d.Encode( 1e21, d.DEGREE, 0), "1e21");
106 | });
107 |
108 | });
109 | });
110 |
--------------------------------------------------------------------------------
/dms/types/geographiclib-dms.d.ts:
--------------------------------------------------------------------------------
1 | export declare const NONE: 0;
2 | export declare const LATITUDE: 1;
3 | export declare const LONGITUDE: 2;
4 | export declare const AZIMUTH: 3;
5 |
6 | export declare const DEGREE: 0;
7 | export declare const MINUTE: 1;
8 | export declare const SECOND: 2;
9 |
10 | export type DMSHemisphereIndicator = 0 | 1 | 2 | 3;
11 | export type DMSTrailingComponent = 0 | 1 | 2;
12 |
13 | export declare const Decode: (dms: string) => {
14 | val: number;
15 | ind: DMSHemisphereIndicator;
16 | };
17 |
18 | export declare const DecodeLatLon: (
19 | stra: string,
20 | strb: string,
21 | longfirst?: boolean // default = false
22 | ) => {
23 | lat: number;
24 | lon: number;
25 | };
26 |
27 | export declare const DecodeAngle: (angstr: string) => number;
28 |
29 | export declare const DecodeAzimuth: (azistr: string) => number;
30 |
31 | export declare const Encode: (
32 | angle: number,
33 | trailing: DMSTrailingComponent,
34 | prec: number,
35 | ind?: DMSHemisphereIndicator // default = NONE
36 | ) => string;
37 |
--------------------------------------------------------------------------------
/doc/.htaccess:
--------------------------------------------------------------------------------
1 | Options +Indexes +FollowSymLinks
2 | IndexOptions FancyIndexing SuppressSize SuppressDescription
3 | IndexOrderDefault Descending Date
4 | IndexIgnore HEADER.html FOOTER.html
5 | ReadmeName FOOTER.html
6 |
--------------------------------------------------------------------------------
/doc/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | configure_file (GeographicLib.md.in GeographicLib.md @ONLY)
2 |
3 | set (GEODSOURCES
4 | ${DESTGEOD}/src/Math.js
5 | ${DESTGEOD}/src/Geodesic.js
6 | ${DESTGEOD}/src/GeodesicLine.js
7 | ${DESTGEOD}/src/PolygonArea.js)
8 |
9 | set (DMSSOURCES
10 | ${DESTDMS}/src/DMS.js)
11 |
12 | set (DOCSOURCES GeographicLib.md
13 | tutorials/1-geodesics.md
14 | tutorials/2-interface.md
15 | tutorials/3-examples.md
16 | tutorials/tutorials.json
17 | ${GEODSOURCES} ${DMSSOURCES})
18 |
19 | add_custom_target (doc ALL
20 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/html/index.html)
21 | add_custom_command (OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/html/index.html
22 | DEPENDS ${DOCSOURCES}
23 | COMMAND ${JSDOC} --verbose -d html
24 | -u ${CMAKE_CURRENT_SOURCE_DIR}/tutorials
25 | -c ${CMAKE_CURRENT_SOURCE_DIR}/conf.json
26 | -R ${CMAKE_CURRENT_BINARY_DIR}/GeographicLib.md
27 | ${GEODSOURCES} ${DMSSOURCES} > jsdoc.log
28 | COMMENT "Generating JavaScript documentation tree")
29 |
30 | if (RSYNC)
31 | set (USER karney)
32 | set (DOCROOT $ENV{HOME}/web/geographiclib-web/htdocs/JavaScript)
33 | set (WEBDEPLOY ${USER},geographiclib@web.sourceforge.net:./htdocs)
34 | add_custom_target (stage-doc
35 | COMMAND ${RSYNC} --delete -av
36 | html/ ${DOCROOT}/${PROJECT_VERSION}/
37 | COMMAND cd ${PROJECT_SOURCE_DIR}/doc &&
38 | ${RSYNC} --delete -av HEADER.html FOOTER.html .htaccess ${DOCROOT}/)
39 | add_dependencies (stage-doc doc)
40 |
41 | add_custom_target (deploy-doc
42 | COMMAND ${RSYNC} --delete -av -e ssh ${DOCROOT} ${WEBDEPLOY}/)
43 | endif ()
44 |
--------------------------------------------------------------------------------
/doc/FOOTER.html:
--------------------------------------------------------------------------------
1 | GeographicLib home
2 |