|
1 | 1 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
2 |
| -/* Geodesy representation conversion functions (c) Chris Veness 2002-2019 */ |
| 2 | +/* Geodesy representation conversion functions (c) Chris Veness 2002-2020 */ |
3 | 3 | /* MIT Licence */
|
4 | 4 | /* www.movable-type.co.uk/scripts/latlong.html */
|
5 | 5 | /* www.movable-type.co.uk/scripts/js/geodesy/geodesy-library.html#dms */
|
@@ -285,39 +285,60 @@ class Dms {
|
285 | 285 |
|
286 | 286 |
|
287 | 287 | /**
|
288 |
| - * Constrain degrees to range 0..360 (e.g. for bearings); -1 => 359, 361 => 1. |
| 288 | + * Constrain degrees to range -90..+90 (for latitude); e.g. -91 => -89, 91 => 89. |
289 | 289 | *
|
290 | 290 | * @private
|
291 | 291 | * @param {number} degrees
|
292 |
| - * @returns degrees within range 0..360. |
| 292 | + * @returns degrees within range -90..+90. |
293 | 293 | */
|
294 |
| - static wrap360(degrees) { |
295 |
| - if (0<=degrees && degrees<360) return degrees; // avoid rounding due to arithmetic ops if within range |
296 |
| - return (degrees%360+360) % 360; // sawtooth wave p:360, a:360 |
| 294 | + static wrap90(degrees) { |
| 295 | + if (-90<=degrees && degrees<=90) return degrees; // avoid rounding due to arithmetic ops if within range |
| 296 | + |
| 297 | + // latitude wrapping requires a triangle wave function; a general triangle wave is |
| 298 | + // f(x) = 4a/p ⋅ | (x-p/4)%p - p/2 | - a |
| 299 | + // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator |
| 300 | + // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n' |
| 301 | + const x = degrees, a = 90, p = 360; |
| 302 | + return 4*a/p * Math.abs((((x-p/4)%p)+p)%p - p/2) - a; |
297 | 303 | }
|
298 | 304 |
|
299 | 305 | /**
|
300 |
| - * Constrain degrees to range -180..+180 (e.g. for longitude); -181 => 179, 181 => -179. |
| 306 | + * Constrain degrees to range -180..+180 (for longitude); e.g. -181 => 179, 181 => -179. |
301 | 307 | *
|
302 | 308 | * @private
|
303 | 309 | * @param {number} degrees
|
304 | 310 | * @returns degrees within range -180..+180.
|
305 | 311 | */
|
306 | 312 | static wrap180(degrees) {
|
307 |
| - if (-180<degrees && degrees<=180) return degrees; // avoid rounding due to arithmetic ops if within range |
308 |
| - return (degrees+540)%360-180; // sawtooth wave p:180, a:±180 |
| 313 | + if (-180<=degrees && degrees<=180) return degrees; // avoid rounding due to arithmetic ops if within range |
| 314 | + |
| 315 | + // longitude wrapping requires a sawtooth wave function; a general sawtooth wave is |
| 316 | + // f(x) = (2ax/p - p/2) % p - a |
| 317 | + // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator |
| 318 | + // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n' |
| 319 | + const x = degrees, a = 180, p = 360; |
| 320 | + return (((2*a*x/p - p/2)%p)+p)%p - a; |
309 | 321 | }
|
310 | 322 |
|
311 | 323 | /**
|
312 |
| - * Constrain degrees to range -90..+90 (e.g. for latitude); -91 => -89, 91 => 89. |
| 324 | + * Constrain degrees to range 0..360 (for bearings); e.g. -1 => 359, 361 => 1. |
313 | 325 | *
|
314 | 326 | * @private
|
315 | 327 | * @param {number} degrees
|
316 |
| - * @returns degrees within range -90..+90. |
| 328 | + * @returns degrees within range 0..360. |
317 | 329 | */
|
318 |
| - static wrap90(degrees) { |
319 |
| - if (-90<=degrees && degrees<=90) return degrees; // avoid rounding due to arithmetic ops if within range |
320 |
| - return Math.abs((degrees%360 + 270)%360 - 180) - 90; // triangle wave p:360 a:±90 TODO: fix e.g. -315° |
| 330 | + static wrap360(degrees) { |
| 331 | + if (0<=degrees && degrees<360) return degrees; // avoid rounding due to arithmetic ops if within range |
| 332 | + |
| 333 | + // bearing wrapping requires a sawtooth wave function with a vertical offset equal to the |
| 334 | + // amplitude and a corresponding phase shift; this changes the general sawtooth wave function from |
| 335 | + // f(x) = (2ax/p - p/2) % p - a |
| 336 | + // to |
| 337 | + // f(x) = (2ax/p) % p |
| 338 | + // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator |
| 339 | + // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n' |
| 340 | + const x = degrees, a = 180, p = 360; |
| 341 | + return (((2*a*x/p)%p)+p)%p; |
321 | 342 | }
|
322 | 343 |
|
323 | 344 | }
|
|
0 commit comments