@litejs/sun 26.2.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.
- package/README.md +66 -0
- package/package.json +26 -0
- package/sun.js +119 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
|
|
2
|
+
[1]: https://badgen.net/coveralls/c/github/litejs/sun
|
|
3
|
+
[2]: https://coveralls.io/r/litejs/sun
|
|
4
|
+
[3]: https://packagephobia.now.sh/badge?p=@litejs/sun
|
|
5
|
+
[4]: https://packagephobia.now.sh/result?p=@litejs/sun
|
|
6
|
+
[5]: https://badgen.net/badge/icon/Buy%20Me%20A%20Tea/orange?icon=kofi&label
|
|
7
|
+
[6]: https://www.buymeacoffee.com/lauriro
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
LiteJS Sun – [![Coverage][1]][2] [![Size][3]][4] [![Buy Me A Tea][5]][6]
|
|
11
|
+
==========
|
|
12
|
+
|
|
13
|
+
Sun position and timing calculations based on NOAA solar equations.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
$ npm install @litejs/sun
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
import { sun } from "@litejs/sun"
|
|
21
|
+
|
|
22
|
+
let data = sun(59.437, 24.753, "2025-03-20T12:00:00Z")
|
|
23
|
+
console.log("Sunrise:", data.rise.time, "Sunset:", data.set.time)
|
|
24
|
+
// Sunrise: 2025-03-20T04:22:24.000Z Sunset: 2025-03-20T16:35:43.000Z
|
|
25
|
+
console.log("Azimuth:", data.azimuth, "Elevation:", data.el)
|
|
26
|
+
// Azimuth: 206.16 Elevation: 28.01
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## API
|
|
30
|
+
|
|
31
|
+
### sun(lat, lon, time)
|
|
32
|
+
|
|
33
|
+
Returns an object with the following properties:
|
|
34
|
+
|
|
35
|
+
| Property | Description |
|
|
36
|
+
|----------------|----------------------------------------------|
|
|
37
|
+
| `time` | Date object for the given time |
|
|
38
|
+
| `decl` | Solar declination (degrees) |
|
|
39
|
+
| `dur` | Daylight duration (minutes) |
|
|
40
|
+
| `el` | Solar elevation with refraction (degrees) |
|
|
41
|
+
| `elTrue` | Solar elevation without refraction (degrees) |
|
|
42
|
+
| `eqTime` | Equation of time (minutes) |
|
|
43
|
+
| `hourAngle` | Solar hour angle (degrees) |
|
|
44
|
+
| `azimuth` | Solar azimuth (degrees, lazy) |
|
|
45
|
+
| `dist` | Distance to sun in AU (lazy) |
|
|
46
|
+
| `rightAsc` | Right ascension (degrees, lazy) |
|
|
47
|
+
| `noon` | Solar noon data (lazy) |
|
|
48
|
+
| `rise` | Sunrise data (lazy) |
|
|
49
|
+
| `set` | Sunset data (lazy) |
|
|
50
|
+
|
|
51
|
+
The `noon`, `rise`, and `set` getters return objects with the same structure, with `time` refined to the exact event.
|
|
52
|
+
During polar day or polar night, `rise` and `set` find the nearest event, which may be days or months away.
|
|
53
|
+
Returns `null` only at extreme latitudes (e.g., the poles) where no sunrise or sunset occurs within a year.
|
|
54
|
+
|
|
55
|
+
## Contributing
|
|
56
|
+
|
|
57
|
+
Follow [Coding Style Guide](https://github.com/litejs/litejs/wiki/Style-Guide),
|
|
58
|
+
run tests `npm install; npm test`.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
> Copyright (c) 2025 Lauri Rooden <lauri@rooden.ee>
|
|
62
|
+
[MIT License](https://litejs.com/MIT-LICENSE.txt) |
|
|
63
|
+
[GitHub repo](https://github.com/litejs/sun) |
|
|
64
|
+
[npm package](https://npmjs.org/package/@litejs/sun) |
|
|
65
|
+
[Buy Me A Tea][6]
|
|
66
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@litejs/sun",
|
|
3
|
+
"version": "26.2.0",
|
|
4
|
+
"description": "Sun position and timing calculations based on NOAA solar equations",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Lauri Rooden <lauri@rooden.ee>",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"astronomy",
|
|
9
|
+
"sun",
|
|
10
|
+
"sunrise",
|
|
11
|
+
"sunset",
|
|
12
|
+
"litejs"
|
|
13
|
+
],
|
|
14
|
+
"main": "sun.js",
|
|
15
|
+
"files": [],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "lj t test/*.js"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/litejs/sun.git"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@litejs/cli": "25.1.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/sun.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
!function(exports, Math) {
|
|
2
|
+
exports.sun = function sun(lat, lon, time, recalc) {
|
|
3
|
+
time = new Date(time)
|
|
4
|
+
|
|
5
|
+
var tmp
|
|
6
|
+
, acos = Math.acos
|
|
7
|
+
, cos = Math.cos
|
|
8
|
+
, sin = Math.sin
|
|
9
|
+
, tan = Math.tan
|
|
10
|
+
, PI = Math.PI
|
|
11
|
+
, rad = PI / 180
|
|
12
|
+
, dayMs = 86400000
|
|
13
|
+
, startOfDay = Math.floor(time / dayMs) * dayMs
|
|
14
|
+
|
|
15
|
+
// Julian Century
|
|
16
|
+
, JC = (time / dayMs - 10957.5) / 36525
|
|
17
|
+
, cosLat = cos(rad * lat)
|
|
18
|
+
, sinLat = sin(rad * lat)
|
|
19
|
+
|
|
20
|
+
// Mean Longitude of the Sun
|
|
21
|
+
, lonMean = rad * (280.46646 + JC * (36000.76983 + JC*0.0003032))
|
|
22
|
+
// Mean Anomaly of the Sun
|
|
23
|
+
, anomMean = rad * (357.52911 + JC * (35999.05029 - 0.0001537 * JC))
|
|
24
|
+
// Mean Obliquity of the Ecliptic (degrees)
|
|
25
|
+
, obliqMean = 23 + (26 + (21.448 - JC*(46.8150 + JC*(0.00059 - JC*0.001813)))/60)/60
|
|
26
|
+
// Corrected Obliquity
|
|
27
|
+
, obliqCorr = rad * (obliqMean + 0.00256 * cos(tmp = rad * (125.04 - 1934.136 * JC)))
|
|
28
|
+
|
|
29
|
+
// Sun's Equation of Center
|
|
30
|
+
, sunEqCtr = sin(anomMean) * (1.914602 - JC * (0.004817 + 0.000014 * JC)) + sin(2 * anomMean) * (0.019993 - 0.000101 * JC) + sin(3 * anomMean) * 0.000289
|
|
31
|
+
|
|
32
|
+
// Apparent Longitude of the Sun
|
|
33
|
+
, lonApp = lonMean + rad * (sunEqCtr - 0.00569 - 0.00478 * sin(tmp))
|
|
34
|
+
|
|
35
|
+
// Declination of the Sun
|
|
36
|
+
, declRad = Math.asin(sin(obliqCorr) * sin(lonApp))
|
|
37
|
+
, sinDecl = sin(declRad)
|
|
38
|
+
, cosDecl = cos(declRad)
|
|
39
|
+
|
|
40
|
+
// Eccentricity of Earth's Orbit
|
|
41
|
+
, earthEccent = 0.016708634 - JC * (0.000042037 + 0.0000001267 * JC)
|
|
42
|
+
, varY = (tmp = tan(obliqCorr / 2)) * tmp
|
|
43
|
+
|
|
44
|
+
// Equation of Time (minutes)
|
|
45
|
+
, eqTime = 4 * (
|
|
46
|
+
varY * sin(2 * lonMean) -
|
|
47
|
+
2 * earthEccent * sin(anomMean) +
|
|
48
|
+
4 * earthEccent * varY * sin(anomMean) * cos(2 * lonMean) -
|
|
49
|
+
0.5 * varY * varY * sin(4 * lonMean) -
|
|
50
|
+
1.25 * earthEccent * earthEccent * sin(2 * anomMean)
|
|
51
|
+
) / rad
|
|
52
|
+
|
|
53
|
+
// Hour Angle at Sunrise (degrees)
|
|
54
|
+
, hourAngleRise = acos((cos(rad * 90.833) - sinLat * sinDecl) / (cosLat * cosDecl)) / rad
|
|
55
|
+
|
|
56
|
+
// True Solar Time (minutes)
|
|
57
|
+
, sunTime = (time / 60000 + eqTime + 4 * lon) % 1440
|
|
58
|
+
, hourAngle = sunTime < 0 ? sunTime / 4 + 180 : sunTime / 4 - 180
|
|
59
|
+
|
|
60
|
+
// Solar Zenith/Elevation
|
|
61
|
+
, cosZenith = sinLat * sinDecl + cosLat * cosDecl * cos(rad * hourAngle)
|
|
62
|
+
, elTrue = 90 - acos(cosZenith) / rad
|
|
63
|
+
// Atmospheric Refraction Correction (degrees)
|
|
64
|
+
, atmCorr = (
|
|
65
|
+
elTrue > 85 ? 0 : (tmp = tan(rad * elTrue),
|
|
66
|
+
elTrue > 5 ? 58.1 / tmp - 0.07 / (tmp*tmp*tmp) + 0.000086 / (tmp*tmp*tmp*tmp*tmp) :
|
|
67
|
+
elTrue > -0.575 ? 1735 + elTrue * (-518.2 + elTrue * (103.4 + elTrue * (-12.79 + elTrue * 0.711))) :
|
|
68
|
+
-20.774 / tmp
|
|
69
|
+
) / 3600
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
// Solar Noon (minutes)
|
|
73
|
+
, noon = 720 - 4 * lon - eqTime
|
|
74
|
+
// Sunrise & Sunset Time (minutes)
|
|
75
|
+
, rise = noon - hourAngleRise * 4
|
|
76
|
+
, set = noon + hourAngleRise * 4
|
|
77
|
+
|
|
78
|
+
if (recalc) {
|
|
79
|
+
tmp = Math.round((recalc === "rise" ? rise : recalc === "set" ? set : noon) * 60) * 1000
|
|
80
|
+
time.setTime(startOfDay + tmp)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
time,
|
|
85
|
+
decl: declRad / rad,
|
|
86
|
+
dur: 8 * hourAngleRise, // Total Sunlight Duration (minutes)
|
|
87
|
+
el: elTrue + atmCorr, // Solar Elevation (Corrected for Refraction)
|
|
88
|
+
elTrue,
|
|
89
|
+
eqTime,
|
|
90
|
+
hourAngle,
|
|
91
|
+
get azimuth() {
|
|
92
|
+
return (tmp = acos((sinLat * cosZenith - sinDecl) / (cosLat * Math.sqrt(1 - cosZenith * cosZenith))), hourAngle > 0 ? tmp + PI : 3 * PI - tmp) * 180 / PI % 360
|
|
93
|
+
},
|
|
94
|
+
get dist() {
|
|
95
|
+
return 1.000001018 * (1 - earthEccent * earthEccent) / (1 + earthEccent * cos(anomMean + rad * sunEqCtr)) // Distance to Sun (Astronomical Units)
|
|
96
|
+
},
|
|
97
|
+
get rightAsc() {
|
|
98
|
+
return Math.atan2(cos(obliqCorr) * sin(lonApp), cos(lonApp)) / rad // Right Ascension of the Sun (degrees)
|
|
99
|
+
},
|
|
100
|
+
get noon() {
|
|
101
|
+
return findNext("noon", noon, -1)
|
|
102
|
+
},
|
|
103
|
+
get rise() {
|
|
104
|
+
return findNext("rise", rise, 1)
|
|
105
|
+
},
|
|
106
|
+
get set() {
|
|
107
|
+
return findNext("set", set, -1)
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function findNext(key, minutes, inc) {
|
|
112
|
+
if (minutes === minutes) return sun(lat, lon, startOfDay + minutes * 60000, key)
|
|
113
|
+
var day = startOfDay, i = 366, d = (time - Date.UTC(time.getUTCFullYear(), 0, 0)) / dayMs
|
|
114
|
+
for (inc *= (lat > 66.4 && d > 79 && d < 267 || lat < -66.4 && (d < 83 || d > 263)) ? -dayMs : dayMs; i--; )
|
|
115
|
+
if ((d = sun(lat, lon, day += inc)).dur === d.dur) return d[key]
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}(this, Math) // jshint ignore:line
|