@hebcal/noaa 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1067 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NOAACalculator = exports.GeoLocation = void 0;
4
+ const temporal_polyfill_1 = require("temporal-polyfill");
5
+ /**
6
+ * java.lang.Math.toRadians
7
+ * @param degrees
8
+ */
9
+ function degreesToRadians(degrees) {
10
+ return (degrees * Math.PI) / 180;
11
+ }
12
+ /**
13
+ * java.lang.Math.toDegrees
14
+ * @param radians
15
+ */
16
+ function radiansToDegrees(radians) {
17
+ return (radians * 180) / Math.PI;
18
+ }
19
+ const Long_MIN_VALUE = NaN;
20
+ /**
21
+ * A class that contains location information such as latitude and longitude required for astronomical calculations. The
22
+ * elevation field may not be used by some calculation engines and would be ignored if set. Check the documentation for
23
+ * specific implementations of the {@link AstronomicalCalculator} to see if elevation is calculated as part of the
24
+ * algorithm.
25
+ *
26
+ * @author © Eliyahu Hershfeld 2004 - 2016
27
+ * @version 1.1
28
+ */
29
+ class GeoLocation {
30
+ /**
31
+ * Method to get the elevation in Meters.
32
+ *
33
+ * @return Returns the elevation in Meters.
34
+ */
35
+ getElevation() {
36
+ return this.elevation;
37
+ }
38
+ /**
39
+ * Method to set the elevation in Meters <b>above </b> sea level.
40
+ *
41
+ * @param elevation
42
+ * The elevation to set in Meters. An IllegalArgumentException will be thrown if the value is a negative.
43
+ */
44
+ setElevation(elevation) {
45
+ if (elevation < 0) {
46
+ throw new Error('Elevation cannot be negative');
47
+ }
48
+ this.elevation = elevation;
49
+ }
50
+ constructor(name = 'Greenwich, England', latitude = 51.4772, longitude = 0, elevationOrTimeZoneId, timeZoneId) {
51
+ /**
52
+ * @see #getLocationName()
53
+ * @see #setLocationName(String)
54
+ */
55
+ this.locationName = null;
56
+ let elevation = 0;
57
+ if (timeZoneId) {
58
+ elevation = elevationOrTimeZoneId;
59
+ }
60
+ else {
61
+ timeZoneId = elevationOrTimeZoneId;
62
+ }
63
+ this.setLocationName(name);
64
+ this.setLatitude(latitude);
65
+ this.setLongitude(longitude);
66
+ this.setElevation(elevation);
67
+ this.setTimeZone(timeZoneId);
68
+ }
69
+ setLatitude(latitude) {
70
+ this.latitude = latitude;
71
+ }
72
+ /**
73
+ * @return Returns the latitude.
74
+ */
75
+ getLatitude() {
76
+ return this.latitude;
77
+ }
78
+ setLongitude(longitude) {
79
+ this.longitude = longitude;
80
+ }
81
+ /**
82
+ * @return Returns the longitude.
83
+ */
84
+ getLongitude() {
85
+ return this.longitude;
86
+ }
87
+ /**
88
+ * @return Returns the location name.
89
+ */
90
+ getLocationName() {
91
+ return this.locationName;
92
+ }
93
+ /**
94
+ * @param name
95
+ * The setter method for the display name.
96
+ */
97
+ setLocationName(name) {
98
+ this.locationName = name;
99
+ }
100
+ /**
101
+ * @return Returns the timeZone.
102
+ */
103
+ getTimeZone() {
104
+ return this.timeZoneId;
105
+ }
106
+ /**
107
+ * Method to set the TimeZone. If this is ever set after the GeoLocation is set in the
108
+ * {@link AstronomicalCalendar}, it is critical that
109
+ * {@link AstronomicalCalendar#getCalendar()}.
110
+ * {@link java.util.Calendar#setTimeZone(TimeZone) setTimeZone(TimeZone)} be called in order for the
111
+ * AstronomicalCalendar to output times in the expected offset. This situation will arise if the
112
+ * AstronomicalCalendar is ever {@link AstronomicalCalendar#clone() cloned}.
113
+ *
114
+ * @param timeZone
115
+ * The timeZone to set.
116
+ */
117
+ setTimeZone(timeZoneId) {
118
+ this.timeZoneId = timeZoneId;
119
+ }
120
+ }
121
+ exports.GeoLocation = GeoLocation;
122
+ /**
123
+ * The commonly used average solar refraction. Calendrical Calculations lists a more accurate global average of
124
+ * 34.478885263888294
125
+ */
126
+ const refraction = 34 / 60;
127
+ // private double refraction = 34.478885263888294 / 60d;
128
+ /**
129
+ * The commonly used average solar radius in minutes of a degree.
130
+ */
131
+ const solarRadius = 16 / 60;
132
+ /**
133
+ * The commonly used average earth radius in KM. At this time, this only affects elevation adjustment and not the
134
+ * sunrise and sunset calculations. The value currently defaults to 6356.9 KM.
135
+ */
136
+ const earthRadius = 6356.9; // in KM
137
+ /**
138
+ * Implementation of sunrise and sunset methods to calculate astronomical times based on the <a
139
+ * href="http://noaa.gov">NOAA</a> algorithm. This calculator uses the Java algorithm based on the implementation by <a
140
+ * href="http://noaa.gov">NOAA - National Oceanic and Atmospheric Administration</a>'s <a href =
141
+ * "http://www.srrb.noaa.gov/highlights/sunrise/sunrise.html">Surface Radiation Research Branch</a>. NOAA's <a
142
+ * href="http://www.srrb.noaa.gov/highlights/sunrise/solareqns.PDF">implementation</a> is based on equations from <a
143
+ * href="http://www.willbell.com/math/mc1.htm">Astronomical Algorithms</a> by <a
144
+ * href="http://en.wikipedia.org/wiki/Jean_Meeus">Jean Meeus</a>. Added to the algorithm is an adjustment of the zenith
145
+ * to account for elevation. The algorithm can be found in the <a
146
+ * href="http://en.wikipedia.org/wiki/Sunrise_equation">Wikipedia Sunrise Equation</a> article.
147
+ *
148
+ * @author &copy; Eliyahu Hershfeld 2011 - 2019
149
+ */
150
+ class NOAACalculator {
151
+ /**
152
+ * The getSunrise method Returns a <code>Date</code> representing the
153
+ * {@link AstronomicalCalculator#getElevationAdjustment(double) elevation adjusted} sunrise time. The zenith used
154
+ * for the calculation uses {@link #GEOMETRIC_ZENITH geometric zenith} of 90&deg; plus
155
+ * {@link AstronomicalCalculator#getElevationAdjustment(double)}. This is adjusted by the
156
+ * {@link AstronomicalCalculator} to add approximately 50/60 of a degree to account for 34 archminutes of refraction
157
+ * and 16 archminutes for the sun's radius for a total of {@link AstronomicalCalculator#adjustZenith 90.83333&deg;}.
158
+ * See documentation for the specific implementation of the {@link AstronomicalCalculator} that you are using.
159
+ *
160
+ * @return the <code>Date</code> representing the exact sunrise time. If the calculation can't be computed such as
161
+ * in the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it
162
+ * does not set, a null will be returned. See detailed explanation on top of the page.
163
+ * @see AstronomicalCalculator#adjustZenith
164
+ * @see #getSeaLevelSunrise()
165
+ * @see AstronomicalCalendar#getUTCSunrise
166
+ */
167
+ getSunrise() {
168
+ const sunrise = this.getUTCSunrise0(NOAACalculator.GEOMETRIC_ZENITH);
169
+ if (Number.isNaN(sunrise))
170
+ return null;
171
+ return this.getDateFromTime(sunrise, true);
172
+ }
173
+ /**
174
+ * A method that returns the sunrise without {@link AstronomicalCalculator#getElevationAdjustment(double) elevation
175
+ * adjustment}. Non-sunrise and sunset calculations such as dawn and dusk, depend on the amount of visible light,
176
+ * something that is not affected by elevation. This method returns sunrise calculated at sea level. This forms the
177
+ * base for dawn calculations that are calculated as a dip below the horizon before sunrise.
178
+ *
179
+ * @return the <code>Date</code> representing the exact sea-level sunrise time. If the calculation can't be computed
180
+ * such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one
181
+ * where it does not set, a null will be returned. See detailed explanation on top of the page.
182
+ * @see AstronomicalCalendar#getSunrise
183
+ * @see AstronomicalCalendar#getUTCSeaLevelSunrise
184
+ * @see #getSeaLevelSunset()
185
+ */
186
+ getSeaLevelSunrise() {
187
+ const sunrise = this.getUTCSeaLevelSunrise(NOAACalculator.GEOMETRIC_ZENITH);
188
+ if (Number.isNaN(sunrise))
189
+ return null;
190
+ return this.getDateFromTime(sunrise, true);
191
+ }
192
+ /**
193
+ * A method that returns the beginning of civil twilight (dawn) using a zenith of {@link #CIVIL_ZENITH 96&deg;}.
194
+ *
195
+ * @return The <code>Date</code> of the beginning of civil twilight using a zenith of 96&deg;. If the calculation
196
+ * can't be computed, null will be returned. See detailed explanation on top of the page.
197
+ * @see #CIVIL_ZENITH
198
+ */
199
+ getBeginCivilTwilight() {
200
+ return this.getSunriseOffsetByDegrees(NOAACalculator.CIVIL_ZENITH);
201
+ }
202
+ /**
203
+ * A method that returns the beginning of nautical twilight using a zenith of {@link #NAUTICAL_ZENITH 102&deg;}.
204
+ *
205
+ * @return The <code>Date</code> of the beginning of nautical twilight using a zenith of 102&deg;. If the
206
+ * calculation can't be computed null will be returned. See detailed explanation on top of the page.
207
+ * @see #NAUTICAL_ZENITH
208
+ */
209
+ getBeginNauticalTwilight() {
210
+ return this.getSunriseOffsetByDegrees(NOAACalculator.NAUTICAL_ZENITH);
211
+ }
212
+ /**
213
+ * A method that returns the beginning of astronomical twilight using a zenith of {@link #ASTRONOMICAL_ZENITH
214
+ * 108&deg;}.
215
+ *
216
+ * @return The <code>Date</code> of the beginning of astronomical twilight using a zenith of 108&deg;. If the
217
+ * calculation can't be computed, null will be returned. See detailed explanation on top of the page.
218
+ * @see #ASTRONOMICAL_ZENITH
219
+ */
220
+ getBeginAstronomicalTwilight() {
221
+ return this.getSunriseOffsetByDegrees(NOAACalculator.ASTRONOMICAL_ZENITH);
222
+ }
223
+ /**
224
+ * The getSunset method Returns a <code>Date</code> representing the
225
+ * {@link AstronomicalCalculator#getElevationAdjustment(double) elevation adjusted} sunset time. The zenith used for
226
+ * the calculation uses {@link #GEOMETRIC_ZENITH geometric zenith} of 90&deg; plus
227
+ * {@link AstronomicalCalculator#getElevationAdjustment(double)}. This is adjusted by the
228
+ * {@link AstronomicalCalculator} to add approximately 50/60 of a degree to account for 34 archminutes of refraction
229
+ * and 16 archminutes for the sun's radius for a total of {@link AstronomicalCalculator#adjustZenith 90.83333&deg;}.
230
+ * See documentation for the specific implementation of the {@link AstronomicalCalculator} that you are using. Note:
231
+ * In certain cases the calculates sunset will occur before sunrise. This will typically happen when a timezone
232
+ * other than the local timezone is used (calculating Los Angeles sunset using a GMT timezone for example). In this
233
+ * case the sunset date will be incremented to the following date.
234
+ *
235
+ * @return the <code>Date</code> representing the exact sunset time. If the calculation can't be computed such as in
236
+ * the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it
237
+ * does not set, a null will be returned. See detailed explanation on top of the page.
238
+ * @see AstronomicalCalculator#adjustZenith
239
+ * @see #getSeaLevelSunset()
240
+ * @see AstronomicalCalendar#getUTCSunset
241
+ */
242
+ getSunset() {
243
+ const sunset = this.getUTCSunset0(NOAACalculator.GEOMETRIC_ZENITH);
244
+ if (Number.isNaN(sunset))
245
+ return null;
246
+ return this.getDateFromTime(sunset, false);
247
+ }
248
+ /**
249
+ * A method that returns the sunset without {@link AstronomicalCalculator#getElevationAdjustment(double) elevation
250
+ * adjustment}. Non-sunrise and sunset calculations such as dawn and dusk, depend on the amount of visible light,
251
+ * something that is not affected by elevation. This method returns sunset calculated at sea level. This forms the
252
+ * base for dusk calculations that are calculated as a dip below the horizon after sunset.
253
+ *
254
+ * @return the <code>Date</code> representing the exact sea-level sunset time. If the calculation can't be computed
255
+ * such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one
256
+ * where it does not set, a null will be returned. See detailed explanation on top of the page.
257
+ * @see AstronomicalCalendar#getSunset
258
+ * @see AstronomicalCalendar#getUTCSeaLevelSunset 2see {@link #getSunset()}
259
+ */
260
+ getSeaLevelSunset() {
261
+ const sunset = this.getUTCSeaLevelSunset(NOAACalculator.GEOMETRIC_ZENITH);
262
+ if (Number.isNaN(sunset))
263
+ return null;
264
+ return this.getDateFromTime(sunset, false);
265
+ }
266
+ /**
267
+ * A method that returns the end of civil twilight using a zenith of {@link #CIVIL_ZENITH 96&deg;}.
268
+ *
269
+ * @return The <code>Date</code> of the end of civil twilight using a zenith of {@link #CIVIL_ZENITH 96&deg;}. If
270
+ * the calculation can't be computed, null will be returned. See detailed explanation on top of the page.
271
+ * @see #CIVIL_ZENITH
272
+ */
273
+ getEndCivilTwilight() {
274
+ return this.getSunsetOffsetByDegrees(NOAACalculator.CIVIL_ZENITH);
275
+ }
276
+ /**
277
+ * A method that returns the end of nautical twilight using a zenith of {@link #NAUTICAL_ZENITH 102&deg;}.
278
+ *
279
+ * @return The <code>Date</code> of the end of nautical twilight using a zenith of {@link #NAUTICAL_ZENITH 102&deg;}
280
+ * . If the calculation can't be computed, null will be returned. See detailed explanation on top of the
281
+ * page.
282
+ * @see #NAUTICAL_ZENITH
283
+ */
284
+ getEndNauticalTwilight() {
285
+ return this.getSunsetOffsetByDegrees(NOAACalculator.NAUTICAL_ZENITH);
286
+ }
287
+ /**
288
+ * A method that returns the end of astronomical twilight using a zenith of {@link #ASTRONOMICAL_ZENITH 108&deg;}.
289
+ *
290
+ * @return the <code>Date</code> of the end of astronomical twilight using a zenith of {@link #ASTRONOMICAL_ZENITH
291
+ * 108&deg;}. If the calculation can't be computed, null will be returned. See detailed explanation on top
292
+ * of the page.
293
+ * @see #ASTRONOMICAL_ZENITH
294
+ */
295
+ getEndAstronomicalTwilight() {
296
+ return this.getSunsetOffsetByDegrees(NOAACalculator.ASTRONOMICAL_ZENITH);
297
+ }
298
+ /**
299
+ * A utility method that returns a date offset by the offset time passed in. Please note that the level of light
300
+ * during twilight is not affected by elevation, so if this is being used to calculate an offset before sunrise or
301
+ * after sunset with the intent of getting a rough "level of light" calculation, the sunrise or sunset time passed
302
+ * to this method should be sea level sunrise and sunset.
303
+ *
304
+ * @param time
305
+ * the start time
306
+ * @param offset
307
+ * the offset in milliseconds to add to the time.
308
+ * @return the {@link java.util.Date} with the offset in milliseconds added to it
309
+ */
310
+ static getTimeOffset(time, offset) {
311
+ if (time === null || offset === Long_MIN_VALUE || Number.isNaN(offset)) {
312
+ return null;
313
+ }
314
+ return time.add({ milliseconds: offset });
315
+ }
316
+ /**
317
+ * A utility method that returns the time of an offset by degrees below or above the horizon of
318
+ * {@link #getSunrise() sunrise}. Note that the degree offset is from the vertical, so for a calculation of 14&deg;
319
+ * before sunrise, an offset of 14 + {@link #GEOMETRIC_ZENITH} = 104 would have to be passed as a parameter.
320
+ *
321
+ * @param offsetZenith
322
+ * the degrees before {@link #getSunrise()} to use in the calculation. For time after sunrise use
323
+ * negative numbers. Note that the degree offset is from the vertical, so for a calculation of 14&deg;
324
+ * before sunrise, an offset of 14 + {@link #GEOMETRIC_ZENITH} = 104 would have to be passed as a
325
+ * parameter.
326
+ * @return The {@link java.util.Date} of the offset after (or before) {@link #getSunrise()}. If the calculation
327
+ * can't be computed such as in the Arctic Circle where there is at least one day a year where the sun does
328
+ * not rise, and one where it does not set, a null will be returned. See detailed explanation on top of the
329
+ * page.
330
+ */
331
+ getSunriseOffsetByDegrees(offsetZenith) {
332
+ const dawn = this.getUTCSunrise0(offsetZenith);
333
+ if (Number.isNaN(dawn))
334
+ return null;
335
+ return this.getDateFromTime(dawn, true);
336
+ }
337
+ /**
338
+ * A utility method that returns the time of an offset by degrees below or above the horizon of {@link #getSunset()
339
+ * sunset}. Note that the degree offset is from the vertical, so for a calculation of 14&deg; after sunset, an
340
+ * offset of 14 + {@link #GEOMETRIC_ZENITH} = 104 would have to be passed as a parameter.
341
+ *
342
+ * @param offsetZenith
343
+ * the degrees after {@link #getSunset()} to use in the calculation. For time before sunset use negative
344
+ * numbers. Note that the degree offset is from the vertical, so for a calculation of 14&deg; after
345
+ * sunset, an offset of 14 + {@link #GEOMETRIC_ZENITH} = 104 would have to be passed as a parameter.
346
+ * @return The {@link java.util.Date}of the offset after (or before) {@link #getSunset()}. If the calculation can't
347
+ * be computed such as in the Arctic Circle where there is at least one day a year where the sun does not
348
+ * rise, and one where it does not set, a null will be returned. See detailed explanation on top of the
349
+ * page.
350
+ */
351
+ getSunsetOffsetByDegrees(offsetZenith) {
352
+ const sunset = this.getUTCSunset0(offsetZenith);
353
+ if (Number.isNaN(sunset))
354
+ return null;
355
+ return this.getDateFromTime(sunset, false);
356
+ }
357
+ /**
358
+ * A constructor that takes in <a href="http://en.wikipedia.org/wiki/Geolocation">geolocation</a> information as a
359
+ * parameter. The default {@link AstronomicalCalculator#getDefault() AstronomicalCalculator} used for solar
360
+ * calculations is the the {@link NOAACalculator}.
361
+ *
362
+ * @param geoLocation
363
+ * The location information used for calculating astronomical sun times.
364
+ *
365
+ * @see #setAstronomicalCalculator(AstronomicalCalculator) for changing the calculator class.
366
+ */
367
+ constructor(geoLocation, date) {
368
+ this.date = date;
369
+ this.geoLocation = geoLocation;
370
+ }
371
+ /**
372
+ * A method that returns the sunrise in UTC time without correction for time zone offset from GMT and without using
373
+ * daylight savings time.
374
+ *
375
+ * @param zenith
376
+ * the degrees below the horizon. For time after sunrise use negative numbers.
377
+ * @return The time in the format: 18.75 for 18:45:00 UTC/GMT. If the calculation can't be computed such as in the
378
+ * Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
379
+ * not set, {@link Double#NaN} will be returned. See detailed explanation on top of the page.
380
+ */
381
+ getUTCSunrise0(zenith) {
382
+ return this.getUTCSunrise(this.getAdjustedDate(), this.geoLocation, zenith, true);
383
+ }
384
+ /**
385
+ * A method that returns the sunrise in UTC time without correction for time zone offset from GMT and without using
386
+ * daylight savings time. Non-sunrise and sunset calculations such as dawn and dusk, depend on the amount of visible
387
+ * light, something that is not affected by elevation. This method returns UTC sunrise calculated at sea level. This
388
+ * forms the base for dawn calculations that are calculated as a dip below the horizon before sunrise.
389
+ *
390
+ * @param zenith
391
+ * the degrees below the horizon. For time after sunrise use negative numbers.
392
+ * @return The time in the format: 18.75 for 18:45:00 UTC/GMT. If the calculation can't be computed such as in the
393
+ * Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
394
+ * not set, {@link Double#NaN} will be returned. See detailed explanation on top of the page.
395
+ * @see AstronomicalCalendar#getUTCSunrise
396
+ * @see AstronomicalCalendar#getUTCSeaLevelSunset
397
+ */
398
+ getUTCSeaLevelSunrise(zenith) {
399
+ return this.getUTCSunrise(this.getAdjustedDate(), this.geoLocation, zenith, false);
400
+ }
401
+ /**
402
+ * A method that returns the sunset in UTC time without correction for time zone offset from GMT and without using
403
+ * daylight savings time.
404
+ *
405
+ * @param zenith
406
+ * the degrees below the horizon. For time after sunset use negative numbers.
407
+ * @return The time in the format: 18.75 for 18:45:00 UTC/GMT. If the calculation can't be computed such as in the
408
+ * Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
409
+ * not set, {@link Double#NaN} will be returned. See detailed explanation on top of the page.
410
+ * @see AstronomicalCalendar#getUTCSeaLevelSunset
411
+ */
412
+ getUTCSunset0(zenith) {
413
+ return this.getUTCSunset(this.getAdjustedDate(), this.geoLocation, zenith, true);
414
+ }
415
+ /**
416
+ * A method that returns the sunset in UTC time without correction for elevation, time zone offset from GMT and
417
+ * without using daylight savings time. Non-sunrise and sunset calculations such as dawn and dusk, depend on the
418
+ * amount of visible light, something that is not affected by elevation. This method returns UTC sunset calculated
419
+ * at sea level. This forms the base for dusk calculations that are calculated as a dip below the horizon after
420
+ * sunset.
421
+ *
422
+ * @param zenith
423
+ * the degrees below the horizon. For time before sunset use negative numbers.
424
+ * @return The time in the format: 18.75 for 18:45:00 UTC/GMT. If the calculation can't be computed such as in the
425
+ * Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
426
+ * not set, {@link Double#NaN} will be returned. See detailed explanation on top of the page.
427
+ * @see AstronomicalCalendar#getUTCSunset
428
+ * @see AstronomicalCalendar#getUTCSeaLevelSunrise
429
+ */
430
+ getUTCSeaLevelSunset(zenith) {
431
+ return this.getUTCSunset(this.getAdjustedDate(), this.geoLocation, zenith, false);
432
+ }
433
+ /**
434
+ * Adjusts the <code>Calendar</code> to deal with edge cases where the location crosses the antimeridian.
435
+ *
436
+ * @see GeoLocation#getAntimeridianAdjustment()
437
+ * @return the adjusted Calendar
438
+ */
439
+ getAdjustedDate() {
440
+ return this.date;
441
+ }
442
+ /**
443
+ * Method to return the adjustment to the zenith required to account for the elevation. Since a person at a higher
444
+ * elevation can see farther below the horizon, the calculation for sunrise / sunset is calculated below the horizon
445
+ * used at sea level. This is only used for sunrise and sunset and not times before or after it such as
446
+ * {@link AstronomicalCalendar#getBeginNauticalTwilight() nautical twilight} since those
447
+ * calculations are based on the level of available light at the given dip below the horizon, something that is not
448
+ * affected by elevation, the adjustment should only made if the zenith == 90&deg; {@link #adjustZenith adjusted}
449
+ * for refraction and solar radius. The algorithm used is
450
+ *
451
+ * <pre>
452
+ * elevationAdjustment = Math.toDegrees(Math.acos(earthRadiusInMeters / (earthRadiusInMeters + elevationMeters)));
453
+ * </pre>
454
+ *
455
+ * The source of this algorithm is <a href="http://www.calendarists.com">Calendrical Calculations</a> by Edward M.
456
+ * Reingold and Nachum Dershowitz. An alternate algorithm that produces an almost identical (but not accurate)
457
+ * result found in Ma'aglay Tzedek by Moishe Kosower and other sources is:
458
+ *
459
+ * <pre>
460
+ * elevationAdjustment = 0.0347 * Math.sqrt(elevationMeters);
461
+ * </pre>
462
+ *
463
+ * @param elevation
464
+ * elevation in Meters.
465
+ * @return the adjusted zenith
466
+ */
467
+ getElevationAdjustment(elevation) {
468
+ // double elevationAdjustment = 0.0347 * Math.sqrt(elevation);
469
+ const elevationAdjustment = radiansToDegrees(Math.acos(earthRadius / (earthRadius + elevation / 1000)));
470
+ return elevationAdjustment;
471
+ }
472
+ /**
473
+ * Adjusts the zenith of astronomical sunrise and sunset to account for solar refraction, solar radius and
474
+ * elevation. The value for Sun's zenith and true rise/set Zenith (used in this class and subclasses) is the angle
475
+ * that the center of the Sun makes to a line perpendicular to the Earth's surface. If the Sun were a point and the
476
+ * Earth were without an atmosphere, true sunset and sunrise would correspond to a 90&deg; zenith. Because the Sun
477
+ * is not a point, and because the atmosphere refracts light, this 90&deg; zenith does not, in fact, correspond to
478
+ * true sunset or sunrise, instead the centre of the Sun's disk must lie just below the horizon for the upper edge
479
+ * to be obscured. This means that a zenith of just above 90&deg; must be used. The Sun subtends an angle of 16
480
+ * minutes of arc (this can be changed via the {@link #setSolarRadius(double)} method , and atmospheric refraction
481
+ * accounts for 34 minutes or so (this can be changed via the {@link #setRefraction(double)} method), giving a total
482
+ * of 50 arcminutes. The total value for ZENITH is 90+(5/6) or 90.8333333&deg; for true sunrise/sunset. Since a
483
+ * person at an elevation can see blow the horizon of a person at sea level, this will also adjust the zenith to
484
+ * account for elevation if available. Note that this will only adjust the value if the zenith is exactly 90 degrees.
485
+ * For values below and above this no correction is done. As an example, astronomical twilight is when the sun is
486
+ * 18&deg; below the horizon or {@link AstronomicalCalendar#ASTRONOMICAL_ZENITH 108&deg;
487
+ * below the zenith}. This is traditionally calculated with none of the above mentioned adjustments. The same goes
488
+ * for various <em>tzais</em> and <em>alos</em> times such as the
489
+ * {@link ZmanimCalendar#ZENITH_16_POINT_1 16.1&deg;} dip used in
490
+ * {@link ComplexZmanimCalendar#getAlos16Point1Degrees()}.
491
+ *
492
+ * @param zenith
493
+ * the azimuth below the vertical zenith of 90&deg;. For sunset typically the {@link #adjustZenith
494
+ * zenith} used for the calculation uses geometric zenith of 90&deg; and {@link #adjustZenith adjusts}
495
+ * this slightly to account for solar refraction and the sun's radius. Another example would be
496
+ * {@link AstronomicalCalendar#getEndNauticalTwilight()} that passes
497
+ * {@link AstronomicalCalendar#NAUTICAL_ZENITH} to this method.
498
+ * @param elevation
499
+ * elevation in Meters.
500
+ * @return The zenith adjusted to include the {@link #getSolarRadius sun's radius}, {@link #getRefraction
501
+ * refraction} and {@link #getElevationAdjustment elevation} adjustment. This will only be adjusted for
502
+ * sunrise and sunset (if the zenith == 90&deg;)
503
+ * @see #getElevationAdjustment(double)
504
+ */
505
+ adjustZenith(zenith, elevation) {
506
+ let adjustedZenith = zenith;
507
+ if (zenith === NOAACalculator.GEOMETRIC_ZENITH) {
508
+ // only adjust if it is exactly sunrise or sunset
509
+ adjustedZenith =
510
+ zenith +
511
+ (solarRadius + refraction + this.getElevationAdjustment(elevation));
512
+ }
513
+ return adjustedZenith;
514
+ }
515
+ /**
516
+ * @see AstronomicalCalculator#getUTCSunrise(Calendar, GeoLocation, double, boolean)
517
+ */
518
+ getUTCSunrise(date, geoLocation, zenith, adjustForElevation) {
519
+ const elevation = adjustForElevation
520
+ ? geoLocation.getElevation()
521
+ : 0;
522
+ const adjustedZenith = this.adjustZenith(zenith, elevation);
523
+ let sunrise = NOAACalculator.getSunriseUTC(NOAACalculator.getJulianDay(date), geoLocation.getLatitude(), -geoLocation.getLongitude(), adjustedZenith);
524
+ sunrise = sunrise / 60;
525
+ // ensure that the time is >= 0 and < 24
526
+ while (sunrise < 0) {
527
+ sunrise += 24;
528
+ }
529
+ while (sunrise >= 24) {
530
+ sunrise -= 24;
531
+ }
532
+ return sunrise;
533
+ }
534
+ /**
535
+ * @see AstronomicalCalculator#getUTCSunset(Calendar, GeoLocation, double, boolean)
536
+ */
537
+ getUTCSunset(date, geoLocation, zenith, adjustForElevation) {
538
+ const elevation = adjustForElevation
539
+ ? geoLocation.getElevation()
540
+ : 0;
541
+ const adjustedZenith = this.adjustZenith(zenith, elevation);
542
+ let sunset = NOAACalculator.getSunsetUTC(NOAACalculator.getJulianDay(date), geoLocation.getLatitude(), -geoLocation.getLongitude(), adjustedZenith);
543
+ sunset = sunset / 60;
544
+ // ensure that the time is >= 0 and < 24
545
+ while (sunset < 0) {
546
+ sunset += 24;
547
+ }
548
+ while (sunset >= 24) {
549
+ sunset -= 24;
550
+ }
551
+ return sunset;
552
+ }
553
+ /**
554
+ * A utility method that will allow the calculation of a temporal (solar) hour based on the sunrise and sunset
555
+ * passed as parameters to this method. An example of the use of this method would be the calculation of a
556
+ * non-elevation adjusted temporal hour by passing in {@link #getSeaLevelSunrise() sea level sunrise} and
557
+ * {@link #getSeaLevelSunset() sea level sunset} as parameters.
558
+ *
559
+ * @param startOfday
560
+ * The start of the day.
561
+ * @param endOfDay
562
+ * The end of the day.
563
+ *
564
+ * @return the <code>long</code> millisecond length of the temporal hour. If the calculation can't be computed a
565
+ * {@link Long#MIN_VALUE} will be returned. See detailed explanation on top of the page.
566
+ *
567
+ * @see #getTemporalHour()
568
+ */
569
+ getTemporalHour(startOfday = this.getSeaLevelSunrise(), endOfDay = this.getSeaLevelSunset()) {
570
+ if (startOfday === null || endOfDay === null) {
571
+ return Long_MIN_VALUE;
572
+ }
573
+ return (endOfDay.valueOf() - startOfday.valueOf()) / 12;
574
+ }
575
+ /**
576
+ * A method that returns sundial or solar noon. It occurs when the Sun is <a href
577
+ * ="http://en.wikipedia.org/wiki/Transit_%28astronomy%29">transiting</a> the <a
578
+ * href="http://en.wikipedia.org/wiki/Meridian_%28astronomy%29">celestial meridian</a>. In this class it is
579
+ * calculated as halfway between the sunrise and sunset passed to this method. This time can be slightly off the
580
+ * real transit time due to changes in declination (the lengthening or shortening day).
581
+ *
582
+ * @param startOfDay
583
+ * the start of day for calculating the sun's transit. This can be sea level sunrise, visual sunrise (or
584
+ * any arbitrary start of day) passed to this method.
585
+ * @param endOfDay
586
+ * the end of day for calculating the sun's transit. This can be sea level sunset, visual sunset (or any
587
+ * arbitrary end of day) passed to this method.
588
+ *
589
+ * @return the <code>Date</code> representing Sun's transit. If the calculation can't be computed such as in the
590
+ * Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
591
+ * not set, null will be returned. See detailed explanation on top of the page.
592
+ */
593
+ getSunTransit(startOfDay = this.getSeaLevelSunrise(), endOfDay = this.getSeaLevelSunset()) {
594
+ const temporalHour = this.getTemporalHour(startOfDay, endOfDay);
595
+ return NOAACalculator.getTimeOffset(startOfDay, temporalHour * 6);
596
+ }
597
+ /**
598
+ * A method that returns a <code>Date</code> from the time passed in as a parameter.
599
+ *
600
+ * @param time
601
+ * The time to be set as the time for the <code>Date</code>. The time expected is in the format: 18.75
602
+ * for 6:45:00 PM.
603
+ * @param isSunrise true if the time is sunrise, and false if it is sunset
604
+ * @return The Date.
605
+ */
606
+ getDateFromTime(time, isSunrise) {
607
+ if (Number.isNaN(time)) {
608
+ return null;
609
+ }
610
+ let calculatedTime = time;
611
+ let cal = this.getAdjustedDate();
612
+ // let cal = new Temporal.PlainDate(adj.year, adj.month, adj.day);
613
+ const hours = Math.trunc(calculatedTime); // retain only the hours
614
+ calculatedTime -= hours;
615
+ const minutes = Math.trunc((calculatedTime *= 60)); // retain only the minutes
616
+ calculatedTime -= minutes;
617
+ const seconds = Math.trunc((calculatedTime *= 60)); // retain only the seconds
618
+ calculatedTime -= seconds; // remaining milliseconds
619
+ // Check if a date transition has occurred, or is about to occur - this indicates the date of the event is
620
+ // actually not the target date, but the day prior or after
621
+ const localTimeHours = Math.trunc(this.geoLocation.getLongitude() / 15);
622
+ if (isSunrise && localTimeHours + hours > 18) {
623
+ cal = cal.add({ days: -1 });
624
+ // cal = cal.minus({days: 1});
625
+ }
626
+ else if (!isSunrise && localTimeHours + hours < 6) {
627
+ cal = cal.add({ days: 1 });
628
+ }
629
+ return cal
630
+ .toZonedDateTime({
631
+ timeZone: 'UTC',
632
+ plainTime: new temporal_polyfill_1.Temporal.PlainTime(hours, minutes, seconds, Math.trunc(calculatedTime * 1000)),
633
+ })
634
+ .withTimeZone(this.geoLocation.getTimeZone());
635
+ }
636
+ /**
637
+ * Return the <a href="http://en.wikipedia.org/wiki/Julian_day">Julian day</a> from a Java Calendar
638
+ *
639
+ * @param calendar
640
+ * The Java Calendar
641
+ * @return the Julian day corresponding to the date Note: Number is returned for start of day. Fractional days
642
+ * should be added later.
643
+ */
644
+ static getJulianDay(date) {
645
+ let { year, month } = date;
646
+ const { day } = date;
647
+ if (month <= 2) {
648
+ year -= 1;
649
+ month += 12;
650
+ }
651
+ const a = Math.trunc(year / 100);
652
+ const b = Math.trunc(2 - a + a / 4);
653
+ return (Math.floor(365.25 * (year + 4716)) +
654
+ Math.floor(30.6001 * (month + 1)) +
655
+ day +
656
+ b -
657
+ 1524.5);
658
+ }
659
+ /**
660
+ * Convert <a href="http://en.wikipedia.org/wiki/Julian_day">Julian day</a> to centuries since J2000.0.
661
+ *
662
+ * @param julianDay
663
+ * the Julian Day to convert
664
+ * @return the centuries since 2000 Julian corresponding to the Julian Day
665
+ */
666
+ static getJulianCenturiesFromJulianDay(julianDay) {
667
+ return ((julianDay - NOAACalculator.JULIAN_DAY_JAN_1_2000) /
668
+ NOAACalculator.JULIAN_DAYS_PER_CENTURY);
669
+ }
670
+ /**
671
+ * Convert centuries since J2000.0 to <a href="http://en.wikipedia.org/wiki/Julian_day">Julian day</a>.
672
+ *
673
+ * @param julianCenturies
674
+ * the number of Julian centuries since J2000.0
675
+ * @return the Julian Day corresponding to the Julian centuries passed in
676
+ */
677
+ static getJulianDayFromJulianCenturies(julianCenturies) {
678
+ return (julianCenturies * NOAACalculator.JULIAN_DAYS_PER_CENTURY +
679
+ NOAACalculator.JULIAN_DAY_JAN_1_2000);
680
+ }
681
+ /**
682
+ * Returns the Geometric <a href="http://en.wikipedia.org/wiki/Mean_longitude">Mean Longitude</a> of the Sun.
683
+ *
684
+ * @param julianCenturies
685
+ * the number of Julian centuries since J2000.0
686
+ * @return the Geometric Mean Longitude of the Sun in degrees
687
+ */
688
+ static getSunGeometricMeanLongitude(julianCenturies) {
689
+ let longitude = 280.46646 + julianCenturies * (36000.76983 + 0.0003032 * julianCenturies);
690
+ while (longitude > 360) {
691
+ longitude -= 360;
692
+ }
693
+ while (longitude < 0) {
694
+ longitude += 360;
695
+ }
696
+ return longitude; // in degrees
697
+ }
698
+ /**
699
+ * Returns the Geometric <a href="http://en.wikipedia.org/wiki/Mean_anomaly">Mean Anomaly</a> of the Sun.
700
+ *
701
+ * @param julianCenturies
702
+ * the number of Julian centuries since J2000.0
703
+ * @return the Geometric Mean Anomaly of the Sun in degrees
704
+ */
705
+ static getSunGeometricMeanAnomaly(julianCenturies) {
706
+ return (357.52911 + julianCenturies * (35999.05029 - 0.0001537 * julianCenturies)); // in degrees
707
+ }
708
+ /**
709
+ * Return the <a href="http://en.wikipedia.org/wiki/Eccentricity_%28orbit%29">eccentricity of earth's orbit</a>.
710
+ *
711
+ * @param julianCenturies
712
+ * the number of Julian centuries since J2000.0
713
+ * @return the unitless eccentricity
714
+ */
715
+ static getEarthOrbitEccentricity(julianCenturies) {
716
+ return (0.016708634 -
717
+ julianCenturies * (0.000042037 + 0.0000001267 * julianCenturies)); // unitless
718
+ }
719
+ /**
720
+ * Returns the <a href="http://en.wikipedia.org/wiki/Equation_of_the_center">equation of center</a> for the sun.
721
+ *
722
+ * @param julianCenturies
723
+ * the number of Julian centuries since J2000.0
724
+ * @return the equation of center for the sun in degrees
725
+ */
726
+ static getSunEquationOfCenter(julianCenturies) {
727
+ const m = NOAACalculator.getSunGeometricMeanAnomaly(julianCenturies);
728
+ const mrad = degreesToRadians(m);
729
+ const sinm = Math.sin(mrad);
730
+ const sin2m = Math.sin(mrad + mrad);
731
+ const sin3m = Math.sin(mrad + mrad + mrad);
732
+ return (sinm *
733
+ (1.914602 - julianCenturies * (0.004817 + 0.000014 * julianCenturies)) +
734
+ sin2m * (0.019993 - 0.000101 * julianCenturies) +
735
+ sin3m * 0.000289); // in degrees
736
+ }
737
+ /**
738
+ * Return the true longitude of the sun
739
+ *
740
+ * @param julianCenturies
741
+ * the number of Julian centuries since J2000.0
742
+ * @return the sun's true longitude in degrees
743
+ */
744
+ static getSunTrueLongitude(julianCenturies) {
745
+ const sunLongitude = NOAACalculator.getSunGeometricMeanLongitude(julianCenturies);
746
+ const center = NOAACalculator.getSunEquationOfCenter(julianCenturies);
747
+ return sunLongitude + center; // in degrees
748
+ }
749
+ // /**
750
+ // * Returns the <a href="http://en.wikipedia.org/wiki/True_anomaly">true anamoly</a> of the sun.
751
+ // *
752
+ // * @param julianCenturies
753
+ // * the number of Julian centuries since J2000.0
754
+ // * @return the sun's true anamoly in degrees
755
+ // */
756
+ // private static double getSunTrueAnomaly(double julianCenturies) {
757
+ // double meanAnomaly = getSunGeometricMeanAnomaly(julianCenturies);
758
+ // double equationOfCenter = getSunEquationOfCenter(julianCenturies);
759
+ //
760
+ // return meanAnomaly + equationOfCenter; // in degrees
761
+ // }
762
+ /**
763
+ * Return the apparent longitude of the sun
764
+ *
765
+ * @param julianCenturies
766
+ * the number of Julian centuries since J2000.0
767
+ * @return sun's apparent longitude in degrees
768
+ */
769
+ static getSunApparentLongitude(julianCenturies) {
770
+ const sunTrueLongitude = NOAACalculator.getSunTrueLongitude(julianCenturies);
771
+ const omega = 125.04 - 1934.136 * julianCenturies;
772
+ const lambda = sunTrueLongitude - 0.00569 - 0.00478 * Math.sin(degreesToRadians(omega));
773
+ return lambda; // in degrees
774
+ }
775
+ /**
776
+ * Returns the mean <a href="http://en.wikipedia.org/wiki/Axial_tilt">obliquity of the ecliptic</a> (Axial tilt).
777
+ *
778
+ * @param julianCenturies
779
+ * the number of Julian centuries since J2000.0
780
+ * @return the mean obliquity in degrees
781
+ */
782
+ static getMeanObliquityOfEcliptic(julianCenturies) {
783
+ const seconds = 21.448 -
784
+ julianCenturies *
785
+ (46.815 + julianCenturies * (0.00059 - julianCenturies * 0.001813));
786
+ return 23 + (26 + seconds / 60) / 60; // in degrees
787
+ }
788
+ /**
789
+ * Returns the corrected <a href="http://en.wikipedia.org/wiki/Axial_tilt">obliquity of the ecliptic</a> (Axial
790
+ * tilt).
791
+ *
792
+ * @param julianCenturies
793
+ * the number of Julian centuries since J2000.0
794
+ * @return the corrected obliquity in degrees
795
+ */
796
+ static getObliquityCorrection(julianCenturies) {
797
+ const obliquityOfEcliptic = NOAACalculator.getMeanObliquityOfEcliptic(julianCenturies);
798
+ const omega = 125.04 - 1934.136 * julianCenturies;
799
+ return obliquityOfEcliptic + 0.00256 * Math.cos(degreesToRadians(omega)); // in degrees
800
+ }
801
+ /**
802
+ * Return the <a href="http://en.wikipedia.org/wiki/Declination">declination</a> of the sun.
803
+ *
804
+ * @param julianCenturies
805
+ * the number of Julian centuries since J2000.0
806
+ * @return
807
+ * the sun's declination in degrees
808
+ */
809
+ static getSunDeclination(julianCenturies) {
810
+ const obliquityCorrection = NOAACalculator.getObliquityCorrection(julianCenturies);
811
+ const lambda = NOAACalculator.getSunApparentLongitude(julianCenturies);
812
+ const sint = Math.sin(degreesToRadians(obliquityCorrection)) *
813
+ Math.sin(degreesToRadians(lambda));
814
+ const theta = radiansToDegrees(Math.asin(sint));
815
+ return theta; // in degrees
816
+ }
817
+ /**
818
+ * Return the <a href="http://en.wikipedia.org/wiki/Equation_of_time">Equation of Time</a> - the difference between
819
+ * true solar time and mean solar time
820
+ *
821
+ * @param julianCenturies
822
+ * the number of Julian centuries since J2000.0
823
+ * @return equation of time in minutes of time
824
+ */
825
+ static getEquationOfTime(julianCenturies) {
826
+ const epsilon = NOAACalculator.getObliquityCorrection(julianCenturies);
827
+ const geomMeanLongSun = NOAACalculator.getSunGeometricMeanLongitude(julianCenturies);
828
+ const eccentricityEarthOrbit = NOAACalculator.getEarthOrbitEccentricity(julianCenturies);
829
+ const geomMeanAnomalySun = NOAACalculator.getSunGeometricMeanAnomaly(julianCenturies);
830
+ let y = Math.tan(degreesToRadians(epsilon) / 2);
831
+ y *= y;
832
+ const sin2l0 = Math.sin(2 * degreesToRadians(geomMeanLongSun));
833
+ const sinm = Math.sin(degreesToRadians(geomMeanAnomalySun));
834
+ const cos2l0 = Math.cos(2 * degreesToRadians(geomMeanLongSun));
835
+ const sin4l0 = Math.sin(4 * degreesToRadians(geomMeanLongSun));
836
+ const sin2m = Math.sin(2 * degreesToRadians(geomMeanAnomalySun));
837
+ const equationOfTime = y * sin2l0 -
838
+ 2 * eccentricityEarthOrbit * sinm +
839
+ 4 * eccentricityEarthOrbit * y * sinm * cos2l0 -
840
+ 0.5 * y * y * sin4l0 -
841
+ 1.25 * eccentricityEarthOrbit * eccentricityEarthOrbit * sin2m;
842
+ return radiansToDegrees(equationOfTime) * 4; // in minutes of time
843
+ }
844
+ /**
845
+ * Return the <a href="http://en.wikipedia.org/wiki/Hour_angle">hour angle</a> of the sun at sunrise for the
846
+ * latitude.
847
+ *
848
+ * @param lat
849
+ * , the latitude of observer in degrees
850
+ * @param solarDec
851
+ * the declination angle of sun in degrees
852
+ * @param zenith
853
+ * the zenith
854
+ * @return hour angle of sunrise in radians
855
+ */
856
+ static getSunHourAngleAtSunrise(lat, solarDec, zenith) {
857
+ const latRad = degreesToRadians(lat);
858
+ const sdRad = degreesToRadians(solarDec);
859
+ return Math.acos(Math.cos(degreesToRadians(zenith)) /
860
+ (Math.cos(latRad) * Math.cos(sdRad)) -
861
+ Math.tan(latRad) * Math.tan(sdRad)); // in radians
862
+ }
863
+ /**
864
+ * Returns the <a href="http://en.wikipedia.org/wiki/Hour_angle">hour angle</a> of the sun at sunset for the
865
+ * latitude. TODO: use - {@link #getSunHourAngleAtSunrise(double, double, double)} implementation to avoid
866
+ * duplication of code.
867
+ *
868
+ * @param lat
869
+ * the latitude of observer in degrees
870
+ * @param solarDec
871
+ * the declination angle of sun in degrees
872
+ * @param zenith
873
+ * the zenith
874
+ * @return the hour angle of sunset in radians
875
+ */
876
+ static getSunHourAngleAtSunset(lat, solarDec, zenith) {
877
+ const latRad = degreesToRadians(lat);
878
+ const sdRad = degreesToRadians(solarDec);
879
+ const hourAngle = Math.acos(Math.cos(degreesToRadians(zenith)) /
880
+ (Math.cos(latRad) * Math.cos(sdRad)) -
881
+ Math.tan(latRad) * Math.tan(sdRad));
882
+ return -hourAngle; // in radians
883
+ }
884
+ /**
885
+ * Return the <a href="http://en.wikipedia.org/wiki/Celestial_coordinate_system">Solar Elevation</a> for the
886
+ * horizontal coordinate system at the given location at the given time. Can be negative if the sun is below the
887
+ * horizon. Not corrected for altitude.
888
+ *
889
+ * @param cal
890
+ * time of calculation
891
+ * @param lat
892
+ * latitude of location for calculation
893
+ * @param lon
894
+ * longitude of location for calculation
895
+ * @return solar elevation in degrees - horizon is 0 degrees, civil twilight is -6 degrees
896
+ */
897
+ static getSolarElevation(date, lat, lon) {
898
+ const julianDay = NOAACalculator.getJulianDay(date.toPlainDate());
899
+ const julianCenturies = NOAACalculator.getJulianCenturiesFromJulianDay(julianDay);
900
+ const equationOfTime = NOAACalculator.getEquationOfTime(julianCenturies);
901
+ let longitude = date.hour + 12 + (date.minute + equationOfTime + date.second / 60) / 60;
902
+ longitude = -((longitude * 360) / 24) % 360;
903
+ const hourAngleRad = degreesToRadians(lon - longitude);
904
+ const declination = NOAACalculator.getSunDeclination(julianCenturies);
905
+ const decRad = degreesToRadians(declination);
906
+ const latRad = degreesToRadians(lat);
907
+ return radiansToDegrees(Math.asin(Math.sin(latRad) * Math.sin(decRad) +
908
+ Math.cos(latRad) * Math.cos(decRad) * Math.cos(hourAngleRad)));
909
+ }
910
+ /**
911
+ * Return the <a href="http://en.wikipedia.org/wiki/Celestial_coordinate_system">Solar Azimuth</a> for the
912
+ * horizontal coordinate system at the given location at the given time. Not corrected for altitude. True south is 0
913
+ * degrees.
914
+ *
915
+ * @param cal
916
+ * time of calculation
917
+ * @param latitude
918
+ * latitude of location for calculation
919
+ * @param lon
920
+ * longitude of location for calculation
921
+ * @return FIXME
922
+ */
923
+ static getSolarAzimuth(date, latitude, lon) {
924
+ const julianDay = NOAACalculator.getJulianDay(date.toPlainDate());
925
+ const julianCenturies = NOAACalculator.getJulianCenturiesFromJulianDay(julianDay);
926
+ const equationOfTime = NOAACalculator.getEquationOfTime(julianCenturies);
927
+ let longitude = date.hour + 12 + (date.minute + equationOfTime + date.second / 60) / 60;
928
+ longitude = -((longitude * 360) / 24) % 360;
929
+ const hourAngleRad = degreesToRadians(lon - longitude);
930
+ const declination = NOAACalculator.getSunDeclination(julianCenturies);
931
+ const decRad = degreesToRadians(declination);
932
+ const latRad = degreesToRadians(latitude);
933
+ return (radiansToDegrees(Math.atan(Math.sin(hourAngleRad) /
934
+ (Math.cos(hourAngleRad) * Math.sin(latRad) -
935
+ Math.tan(decRad) * Math.cos(latRad)))) + 180);
936
+ }
937
+ /**
938
+ * Return the <a href="http://en.wikipedia.org/wiki/Universal_Coordinated_Time">Universal Coordinated Time</a> (UTC)
939
+ * of sunrise for the given day at the given location on earth
940
+ *
941
+ * @param julianDay
942
+ * the Julian day
943
+ * @param latitude
944
+ * the latitude of observer in degrees
945
+ * @param longitude
946
+ * the longitude of observer in degrees
947
+ * @param zenith
948
+ * the zenith
949
+ * @return the time in minutes from zero UTC
950
+ */
951
+ static getSunriseUTC(julianDay, latitude, longitude, zenith) {
952
+ const julianCenturies = NOAACalculator.getJulianCenturiesFromJulianDay(julianDay);
953
+ // Find the time of solar noon at the location, and use that declination. This is better than start of the
954
+ // Julian day
955
+ const noonmin = NOAACalculator.getSolarNoonUTC(julianCenturies, longitude);
956
+ const tnoon = NOAACalculator.getJulianCenturiesFromJulianDay(julianDay + noonmin / 1440);
957
+ // First pass to approximate sunrise (using solar noon)
958
+ let eqTime = NOAACalculator.getEquationOfTime(tnoon);
959
+ let solarDec = NOAACalculator.getSunDeclination(tnoon);
960
+ let hourAngle = NOAACalculator.getSunHourAngleAtSunrise(latitude, solarDec, zenith);
961
+ let delta = longitude - radiansToDegrees(hourAngle);
962
+ let timeDiff = 4 * delta; // in minutes of time
963
+ let timeUTC = 720 + timeDiff - eqTime; // in minutes
964
+ // Second pass includes fractional Julian Day in gamma calc
965
+ const newt = NOAACalculator.getJulianCenturiesFromJulianDay(NOAACalculator.getJulianDayFromJulianCenturies(julianCenturies) +
966
+ timeUTC / 1440);
967
+ eqTime = NOAACalculator.getEquationOfTime(newt);
968
+ solarDec = NOAACalculator.getSunDeclination(newt);
969
+ hourAngle = NOAACalculator.getSunHourAngleAtSunrise(latitude, solarDec, zenith);
970
+ delta = longitude - radiansToDegrees(hourAngle);
971
+ timeDiff = 4 * delta;
972
+ timeUTC = 720 + timeDiff - eqTime; // in minutes
973
+ return timeUTC;
974
+ }
975
+ /**
976
+ * Return the <a href="http://en.wikipedia.org/wiki/Universal_Coordinated_Time">Universal Coordinated Time</a> (UTC)
977
+ * of <a href="http://en.wikipedia.org/wiki/Noon#Solar_noon">solar noon</a> for the given day at the given location
978
+ * on earth.
979
+ *
980
+ * @param julianCenturies
981
+ * the number of Julian centuries since J2000.0
982
+ * @param longitude
983
+ * the longitude of observer in degrees
984
+ * @return the time in minutes from zero UTC
985
+ */
986
+ static getSolarNoonUTC(julianCenturies, longitude) {
987
+ // First pass uses approximate solar noon to calculate eqtime
988
+ const tnoon = NOAACalculator.getJulianCenturiesFromJulianDay(NOAACalculator.getJulianDayFromJulianCenturies(julianCenturies) +
989
+ longitude / 360);
990
+ let eqTime = NOAACalculator.getEquationOfTime(tnoon);
991
+ const solNoonUTC = 720 + longitude * 4 - eqTime; // min
992
+ const newt = NOAACalculator.getJulianCenturiesFromJulianDay(NOAACalculator.getJulianDayFromJulianCenturies(julianCenturies) -
993
+ 0.5 +
994
+ solNoonUTC / 1440);
995
+ eqTime = NOAACalculator.getEquationOfTime(newt);
996
+ return 720 + longitude * 4 - eqTime; // min
997
+ }
998
+ /**
999
+ * Return the <a href="http://en.wikipedia.org/wiki/Universal_Coordinated_Time">Universal Coordinated Time</a> (UTC)
1000
+ * of sunset for the given day at the given location on earth
1001
+ *
1002
+ * @param julianDay
1003
+ * the Julian day
1004
+ * @param latitude
1005
+ * the latitude of observer in degrees
1006
+ * @param longitude
1007
+ * : longitude of observer in degrees
1008
+ * @param zenith
1009
+ * the zenith
1010
+ * @return the time in minutes from zero Universal Coordinated Time (UTC)
1011
+ */
1012
+ static getSunsetUTC(julianDay, latitude, longitude, zenith) {
1013
+ const julianCenturies = NOAACalculator.getJulianCenturiesFromJulianDay(julianDay);
1014
+ // Find the time of solar noon at the location, and use that declination. This is better than start of the
1015
+ // Julian day
1016
+ const noonmin = NOAACalculator.getSolarNoonUTC(julianCenturies, longitude);
1017
+ const tnoon = NOAACalculator.getJulianCenturiesFromJulianDay(julianDay + noonmin / 1440);
1018
+ // First calculates sunrise and approx length of day
1019
+ let eqTime = NOAACalculator.getEquationOfTime(tnoon);
1020
+ let solarDec = NOAACalculator.getSunDeclination(tnoon);
1021
+ let hourAngle = NOAACalculator.getSunHourAngleAtSunset(latitude, solarDec, zenith);
1022
+ let delta = longitude - radiansToDegrees(hourAngle);
1023
+ let timeDiff = 4 * delta;
1024
+ let timeUTC = 720 + timeDiff - eqTime;
1025
+ // Second pass includes fractional Julian Day in gamma calc
1026
+ const newt = NOAACalculator.getJulianCenturiesFromJulianDay(NOAACalculator.getJulianDayFromJulianCenturies(julianCenturies) +
1027
+ timeUTC / 1440);
1028
+ eqTime = NOAACalculator.getEquationOfTime(newt);
1029
+ solarDec = NOAACalculator.getSunDeclination(newt);
1030
+ hourAngle = NOAACalculator.getSunHourAngleAtSunset(latitude, solarDec, zenith);
1031
+ delta = longitude - radiansToDegrees(hourAngle);
1032
+ timeDiff = 4 * delta;
1033
+ timeUTC = 720 + timeDiff - eqTime; // in minutes
1034
+ return timeUTC;
1035
+ }
1036
+ }
1037
+ exports.NOAACalculator = NOAACalculator;
1038
+ /**
1039
+ * The zenith of astronomical sunrise and sunset. The sun is 90&deg; from the vertical 0&deg;
1040
+ */
1041
+ NOAACalculator.GEOMETRIC_ZENITH = 90;
1042
+ /**
1043
+ * Default value for Sun's zenith and true rise/set Zenith (used in this class and subclasses) is the angle that the
1044
+ * center of the Sun makes to a line perpendicular to the Earth's surface. If the Sun were a point and the Earth
1045
+ * were without an atmosphere, true sunset and sunrise would correspond to a 90&deg; zenith. Because the Sun is not
1046
+ * a point, and because the atmosphere refracts light, this 90&deg; zenith does not, in fact, correspond to true
1047
+ * sunset or sunrise, instead the center of the Sun's disk must lie just below the horizon for the upper edge to be
1048
+ * obscured. This means that a zenith of just above 90&deg; must be used. The Sun subtends an angle of 16 minutes of
1049
+ * arc (this can be changed via the {@link #setSunRadius(double)} method , and atmospheric refraction accounts for
1050
+ * 34 minutes or so (this can be changed via the {@link #setRefraction(double)} method), giving a total of 50
1051
+ * arcminutes. The total value for ZENITH is 90+(5/6) or 90.8333333&deg; for true sunrise/sunset.
1052
+ */
1053
+ // const ZENITH: number = GEOMETRIC_ZENITH + 5.0 / 6.0;
1054
+ /** Sun's zenith at civil twilight (96&deg;). */
1055
+ NOAACalculator.CIVIL_ZENITH = 96;
1056
+ /** Sun's zenith at nautical twilight (102&deg;). */
1057
+ NOAACalculator.NAUTICAL_ZENITH = 102;
1058
+ /** Sun's zenith at astronomical twilight (108&deg;). */
1059
+ NOAACalculator.ASTRONOMICAL_ZENITH = 108;
1060
+ /**
1061
+ * The <a href="http://en.wikipedia.org/wiki/Julian_day">Julian day</a> of January 1, 2000
1062
+ */
1063
+ NOAACalculator.JULIAN_DAY_JAN_1_2000 = 2451545;
1064
+ /**
1065
+ * Julian days per century
1066
+ */
1067
+ NOAACalculator.JULIAN_DAYS_PER_CENTURY = 36525;