@johnmorrisdotca/hoshi-stars 0.0.0-stage → 0.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE.md +146 -0
  4. package/README.md +337 -2
  5. package/dist/boundaries.d.ts +95 -0
  6. package/dist/boundaries.js +195 -0
  7. package/dist/constellations.d.ts +72 -0
  8. package/dist/constellations.js +105 -0
  9. package/dist/data/boundaries.d.ts +4 -0
  10. package/dist/data/boundaries.js +449 -0
  11. package/dist/data/constellations.d.ts +1 -0
  12. package/dist/data/constellations.js +91 -0
  13. package/dist/data/lines.d.ts +2 -0
  14. package/dist/data/lines.js +92 -0
  15. package/dist/data/messier.d.ts +2 -0
  16. package/dist/data/messier.js +116 -0
  17. package/dist/data/milky-way.d.ts +5 -0
  18. package/dist/data/milky-way.js +7 -0
  19. package/dist/data/ngc-ic.d.ts +2 -0
  20. package/dist/data/ngc-ic.js +11794 -0
  21. package/dist/data/star-names-ja.d.ts +2 -0
  22. package/dist/data/star-names-ja.js +4 -0
  23. package/dist/data/stars.d.ts +4 -0
  24. package/dist/data/stars.js +5088 -0
  25. package/dist/deep-sky-core.d.ts +123 -0
  26. package/dist/deep-sky-core.js +139 -0
  27. package/dist/deep-sky-ngc-ic.d.ts +34 -0
  28. package/dist/deep-sky-ngc-ic.js +43 -0
  29. package/dist/deep-sky.d.ts +49 -0
  30. package/dist/deep-sky.js +59 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +2 -0
  33. package/dist/lines.d.ts +49 -0
  34. package/dist/lines.js +64 -0
  35. package/dist/milky-way.d.ts +144 -0
  36. package/dist/milky-way.js +242 -0
  37. package/dist/projection.d.ts +127 -0
  38. package/dist/projection.js +284 -0
  39. package/dist/sky.d.ts +202 -0
  40. package/dist/sky.js +283 -0
  41. package/dist/stars.d.ts +162 -0
  42. package/dist/stars.js +195 -0
  43. package/dist/time-scale.d.ts +14 -0
  44. package/dist/time-scale.js +71 -0
  45. package/dist/when.d.ts +16 -0
  46. package/dist/when.js +18 -0
  47. package/package.json +184 -4
@@ -0,0 +1,284 @@
1
+ // Map projections of the celestial sphere for drawing star charts: stereographic, orthographic, gnomonic, azimuthal equidistant, equirectangular, Mollweide and Hammer-Aitoff.
2
+ // Pure maths, no DOM and no dependencies. The formulas are written from J. P. Snyder, "Map Projections: A Working Manual", U.S. Geological Survey Professional Paper 1395 (1987, public domain):
3
+ // the azimuthal family in chapters 20 to 22 and 25, the cylindrical one in chapter 12, Mollweide in chapter 31 and Hammer in chapter 24 (on the unit sphere, with the centre as the origin).
4
+ // The sky is a sphere seen from inside, so the default puts east on the LEFT (right ascension grows toward the left); `mirror: true` gives the map convention with east on the right.
5
+ /**
6
+ * The projections: the five that show a hemisphere or less, then the two that show the whole sky.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { PROJECTIONS } from "@johnmorrisdotca/hoshi-stars/projection";
11
+ *
12
+ * console.log(PROJECTIONS.join(", "));
13
+ * ```
14
+ */
15
+ export const PROJECTIONS = ["stereographic", "orthographic", "gnomonic", "equidistant", "equirectangular", "mollweide", "hammer"];
16
+ const RAD = Math.PI / 180;
17
+ const DEG = 180 / Math.PI;
18
+ const TWO_PI = 2 * Math.PI;
19
+ const SQRT2 = Math.SQRT2;
20
+ // A hair of slack so a point exactly on the edge (90 degrees, the oval's rim) is not lost to rounding.
21
+ const EDGE = 1e-12;
22
+ function finite(name, value) {
23
+ if (typeof value !== "number" || !Number.isFinite(value))
24
+ throw new RangeError(`${name} must be a finite number, got ${String(value)}`);
25
+ return value;
26
+ }
27
+ function sky(name, point) {
28
+ if (typeof point !== "object" || point === null)
29
+ throw new TypeError(`${name} must be an object with rightAscension and declination, got ${String(point)}`);
30
+ const p = point;
31
+ finite(`${name}.rightAscension`, p.rightAscension);
32
+ const declination = finite(`${name}.declination`, p.declination);
33
+ if (declination < -90 || declination > 90)
34
+ throw new RangeError(`${name}.declination must be between -90 and 90 degrees, got ${declination}`);
35
+ return p;
36
+ }
37
+ function setup(kind, options) {
38
+ if (!PROJECTIONS.includes(kind))
39
+ throw new RangeError(`kind must be one of ${PROJECTIONS.map((k) => `"${k}"`).join(", ")}, got "${String(kind)}"`);
40
+ if (typeof options !== "object" || options === null)
41
+ throw new TypeError(`options must be an object with a centre, got ${String(options)}`);
42
+ const centre = sky("options.centre", options.centre);
43
+ const scale = options.scale === undefined ? 1 : finite("options.scale", options.scale);
44
+ if (scale <= 0)
45
+ throw new RangeError(`options.scale must be greater than 0, got ${scale}`);
46
+ const rotation = options.rotation === undefined ? 0 : finite("options.rotation", options.rotation) * RAD;
47
+ const dec0 = centre.declination * RAD;
48
+ return {
49
+ ra0: centre.rightAscension * RAD,
50
+ sinD0: Math.sin(dec0),
51
+ cosD0: Math.cos(dec0),
52
+ dec0,
53
+ scale,
54
+ sinR: Math.sin(rotation),
55
+ cosR: Math.cos(rotation),
56
+ rotated: rotation !== 0,
57
+ mirror: options.mirror === true,
58
+ };
59
+ }
60
+ /** An angle in radians brought to -pi up to pi. */
61
+ function wrapPi(angle) {
62
+ return angle - TWO_PI * Math.round(angle / TWO_PI);
63
+ }
64
+ /** Mollweide's auxiliary angle: with t = 2 theta, t + sin t = pi sin(latitude), by Newton's method (Snyder 31). */
65
+ function mollweideAngle(latitude) {
66
+ const target = Math.PI * Math.sin(latitude);
67
+ const sign = target < 0 ? -1 : 1;
68
+ const wanted = Math.abs(target);
69
+ if (wanted >= Math.PI - 1e-15)
70
+ return sign * Math.PI;
71
+ // Near a pole the root is a triple one, so start from its cube-root form there; elsewhere the latitude doubled is close.
72
+ let t = wanted > 2.9 ? Math.PI - Math.cbrt(6 * (Math.PI - wanted)) : 2 * Math.abs(latitude);
73
+ for (let i = 0; i < 60; i += 1) {
74
+ const step = (t + Math.sin(t) - wanted) / (1 + Math.cos(t));
75
+ t -= step;
76
+ if (Math.abs(step) < 1e-15)
77
+ break;
78
+ }
79
+ return sign * t;
80
+ }
81
+ /**
82
+ * Where a point of the sky lands on the chart.
83
+ *
84
+ * With the default `mirror: false` the chart is the sky seen looking up: north is up and east is to the LEFT, so `x` grows toward decreasing right ascension. Set `mirror: true` for the map convention (east right).
85
+ * `y` grows toward the north (up the chart).
86
+ *
87
+ * @param kind - The projection.
88
+ * @param point - The sky position in degrees (`rightAscension` wraps at 360; `declination` is -90 to 90).
89
+ * @param options - The `centre` that lands at (0, 0), and optionally `scale`, `rotation` and `mirror`.
90
+ * @returns The point on the chart, or `undefined` when the sky position is not on the visible side: the far hemisphere of `orthographic` and `gnomonic` (90 degrees or more from the centre for `gnomonic`), and the antipode of the centre for `stereographic`.
91
+ * @throws RangeError if `kind` is unknown, or a number is not finite, or a declination is outside -90 to 90, or `scale` is not above 0.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * import { project } from "@johnmorrisdotca/hoshi-stars/projection";
96
+ *
97
+ * const orion = { centre: { rightAscension: 83.8, declination: -5.4 }, scale: 300 };
98
+ * const betelgeuse = project("stereographic", { rightAscension: 88.79, declination: 7.41 }, orion);
99
+ * console.log(betelgeuse); // left of the middle (it is east of the centre) and above it
100
+ * ```
101
+ */
102
+ export function project(kind, point, options) {
103
+ const o = setup(kind, options);
104
+ sky("point", point);
105
+ const dec = point.declination * RAD;
106
+ const dl = wrapPi(point.rightAscension * RAD - o.ra0);
107
+ // X is east-positive on the unit sphere, Y is north-positive.
108
+ let X;
109
+ let Y;
110
+ if (kind === "equirectangular") {
111
+ X = dl;
112
+ Y = dec - o.dec0;
113
+ }
114
+ else {
115
+ const sinD = Math.sin(dec);
116
+ const cosD = Math.cos(dec);
117
+ const cosDl = Math.cos(dl);
118
+ // The point in the frame of the centre: u toward the centre, e toward the east, n toward the north.
119
+ const u = o.sinD0 * sinD + o.cosD0 * cosD * cosDl;
120
+ const e = cosD * Math.sin(dl);
121
+ const n = o.cosD0 * sinD - o.sinD0 * cosD * cosDl;
122
+ let k;
123
+ switch (kind) {
124
+ case "orthographic":
125
+ if (u < -EDGE)
126
+ return undefined;
127
+ k = 1;
128
+ break;
129
+ case "stereographic":
130
+ if (1 + u < EDGE)
131
+ return undefined;
132
+ k = 2 / (1 + u);
133
+ break;
134
+ case "gnomonic":
135
+ if (u <= EDGE)
136
+ return undefined;
137
+ k = 1 / u;
138
+ break;
139
+ case "equidistant": {
140
+ const s = Math.hypot(e, n);
141
+ if (s < 1e-300) {
142
+ if (u < 0)
143
+ return finish(o, Math.PI, 0);
144
+ k = 1;
145
+ }
146
+ else {
147
+ k = Math.atan2(s, u) / s;
148
+ }
149
+ break;
150
+ }
151
+ case "mollweide": {
152
+ const lambda = Math.atan2(e, u);
153
+ const t = mollweideAngle(Math.atan2(n, Math.hypot(e, u)));
154
+ return finish(o, ((2 * SQRT2) / Math.PI) * lambda * Math.cos(t / 2), SQRT2 * Math.sin(t / 2));
155
+ }
156
+ default: {
157
+ const lambda = Math.atan2(e, u);
158
+ const cosPhi = Math.hypot(e, u);
159
+ const d = Math.sqrt(1 + cosPhi * Math.cos(lambda / 2));
160
+ return finish(o, (2 * SQRT2 * cosPhi * Math.sin(lambda / 2)) / d, (SQRT2 * n) / d);
161
+ }
162
+ }
163
+ X = k * e;
164
+ Y = k * n;
165
+ }
166
+ return finish(o, X, Y);
167
+ }
168
+ /** Scales, applies east-left or `mirror`, and turns the chart: from east-positive unit-sphere coordinates to the answer. */
169
+ function finish(o, X, Y) {
170
+ const x = (o.mirror ? X : -X) * o.scale;
171
+ const y = Y * o.scale;
172
+ // Adding zero turns -0 into 0, so the centre is exactly (0, 0).
173
+ if (!o.rotated)
174
+ return { x: x + 0, y: y + 0 };
175
+ return { x: x * o.cosR - y * o.sinR + 0, y: x * o.sinR + y * o.cosR + 0 };
176
+ }
177
+ /**
178
+ * The sky position under a point of the chart: the inverse of `project`, with the same `options`.
179
+ *
180
+ * @param kind - The projection.
181
+ * @param point - A point of the chart, in the same units and convention as `project` gives.
182
+ * @param options - The same options that drew the chart.
183
+ * @returns Right ascension (0 up to 360) and declination in degrees, or `undefined` when the point is outside the map: beyond the edge of the orthographic disc, the equidistant disc (radius pi times `scale`), the Mollweide and Hammer ovals, or the equirectangular rectangle.
184
+ * @throws RangeError if `kind` is unknown or a number is not finite.
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * import { project, unproject } from "@johnmorrisdotca/hoshi-stars/projection";
189
+ *
190
+ * const options = { centre: { rightAscension: 279.2, declination: 38.8 }, scale: 200 };
191
+ * const spot = project("orthographic", { rightAscension: 290, declination: 45 }, options)!;
192
+ * console.log(unproject("orthographic", spot, options)); // { rightAscension: 290, declination: 45 }, to rounding
193
+ * ```
194
+ */
195
+ export function unproject(kind, point, options) {
196
+ const o = setup(kind, options);
197
+ if (typeof point !== "object" || point === null)
198
+ throw new TypeError(`point must be an object with x and y, got ${String(point)}`);
199
+ finite("point.x", point.x);
200
+ finite("point.y", point.y);
201
+ // Undo the turn, then the mirror, then the scale, to east-positive unit-sphere coordinates.
202
+ const px = o.rotated ? point.x * o.cosR + point.y * o.sinR : point.x;
203
+ const py = o.rotated ? point.y * o.cosR - point.x * o.sinR : point.y;
204
+ const X = (o.mirror ? px : -px) / o.scale;
205
+ const Y = py / o.scale;
206
+ let u;
207
+ let e;
208
+ let n;
209
+ switch (kind) {
210
+ case "equirectangular": {
211
+ const declination = Y + o.dec0;
212
+ if (Math.abs(X) > Math.PI + EDGE || Math.abs(declination) > Math.PI / 2 + EDGE)
213
+ return undefined;
214
+ return answer(o.ra0 + X, Math.max(-Math.PI / 2, Math.min(Math.PI / 2, declination)));
215
+ }
216
+ case "mollweide": {
217
+ if ((X * X) / 8 + (Y * Y) / 2 > 1 + EDGE)
218
+ return undefined;
219
+ const s = Math.max(-1, Math.min(1, Y / SQRT2));
220
+ const cosT = Math.sqrt(1 - s * s);
221
+ // sin(latitude) = (2 theta + sin 2 theta) / pi, with 2 theta = 2 atan2(sin theta, cos theta)
222
+ const sinPhi = Math.max(-1, Math.min(1, (2 * Math.atan2(s, cosT) + 2 * s * cosT) / Math.PI));
223
+ const lambda = cosT < 1e-300 ? 0 : (Math.PI * X) / (2 * SQRT2 * cosT);
224
+ const cosPhi = Math.sqrt(1 - sinPhi * sinPhi);
225
+ u = cosPhi * Math.cos(lambda);
226
+ e = cosPhi * Math.sin(lambda);
227
+ n = sinPhi;
228
+ break;
229
+ }
230
+ case "hammer": {
231
+ if ((X * X) / 8 + (Y * Y) / 2 > 1 + EDGE)
232
+ return undefined;
233
+ const z2 = Math.max(0.5, 1 - (X * X) / 16 - (Y * Y) / 4);
234
+ const z = Math.sqrt(z2);
235
+ const lambda = 2 * Math.atan2(z * X, 2 * (2 * z2 - 1));
236
+ const sinPhi = Math.max(-1, Math.min(1, z * Y));
237
+ const cosPhi = Math.sqrt(1 - sinPhi * sinPhi);
238
+ u = cosPhi * Math.cos(lambda);
239
+ e = cosPhi * Math.sin(lambda);
240
+ n = sinPhi;
241
+ break;
242
+ }
243
+ default: {
244
+ const rho = Math.hypot(X, Y);
245
+ if (rho < 1e-300)
246
+ return answer(o.ra0, o.dec0);
247
+ let sinC;
248
+ let cosC;
249
+ if (kind === "orthographic") {
250
+ if (rho > 1 + EDGE)
251
+ return undefined;
252
+ sinC = Math.min(rho, 1);
253
+ cosC = Math.sqrt(1 - sinC * sinC);
254
+ }
255
+ else if (kind === "stereographic") {
256
+ sinC = (4 * rho) / (4 + rho * rho);
257
+ cosC = (4 - rho * rho) / (4 + rho * rho);
258
+ }
259
+ else if (kind === "gnomonic") {
260
+ sinC = rho / Math.sqrt(1 + rho * rho);
261
+ cosC = 1 / Math.sqrt(1 + rho * rho);
262
+ }
263
+ else {
264
+ if (rho > Math.PI + EDGE)
265
+ return undefined;
266
+ sinC = Math.sin(rho);
267
+ cosC = Math.cos(rho);
268
+ }
269
+ const f = sinC / rho;
270
+ u = cosC;
271
+ e = X * f;
272
+ n = Y * f;
273
+ }
274
+ }
275
+ const w = o.cosD0 * u - o.sinD0 * n;
276
+ return answer(o.ra0 + Math.atan2(e, w), Math.atan2(o.sinD0 * u + o.cosD0 * n, Math.hypot(e, w)));
277
+ }
278
+ /** Radians to degrees, with right ascension in 0 up to 360. */
279
+ function answer(ra, dec) {
280
+ let rightAscension = (((ra * DEG) % 360) + 360) % 360;
281
+ if (rightAscension >= 360)
282
+ rightAscension = 0;
283
+ return { rightAscension, declination: dec * DEG };
284
+ }
package/dist/sky.d.ts ADDED
@@ -0,0 +1,202 @@
1
+ import { type When } from "./when.ts";
2
+ import type { SkyPoint } from "./projection.ts";
3
+ export type { SkyPoint };
4
+ /**
5
+ * Where a star or a planet is in your sky: degrees above the horizon (negative below it) and degrees round from north through east.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import type { Horizontal } from "@johnmorrisdotca/hoshi-stars/sky";
10
+ *
11
+ * const view: Horizontal = { altitude: 45, azimuth: 90 };
12
+ * console.log(view.altitude, view.azimuth); // 45 degrees up, due east
13
+ * ```
14
+ */
15
+ export interface Horizontal {
16
+ /** Degrees above the horizon, -90 (straight down) to 90 (the zenith). Negative means below the horizon. */
17
+ altitude: number;
18
+ /** Degrees from north through east: 0 north, 90 east, 180 south, 270 west. 0 up to but not including 360; 0 at the zenith and nadir, where it has no meaning. */
19
+ azimuth: number;
20
+ }
21
+ /**
22
+ * Where you stand on Earth, in degrees.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import type { Observer } from "@johnmorrisdotca/hoshi-stars/sky";
27
+ *
28
+ * const vancouver: Observer = { latitude: 49.28, longitude: -123.12 };
29
+ * console.log(vancouver.latitude);
30
+ * ```
31
+ */
32
+ export interface Observer {
33
+ /** Degrees north of the equator, -90 to 90. */
34
+ latitude: number;
35
+ /** Degrees east of Greenwich (west is negative). Any finite number; it wraps at 360. */
36
+ longitude: number;
37
+ /** Metres above sea level; checked but not used, because a star is so far away that height changes its direction by nothing you could measure, and the horizon dip is left out. */
38
+ heightM?: number;
39
+ }
40
+ /**
41
+ * How to read the position and whether to bend the light.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import type { HorizontalOptions } from "@johnmorrisdotca/hoshi-stars/sky";
46
+ *
47
+ * const options: HorizontalOptions = { refraction: true, pressureHpa: 900, temperatureC: -5 };
48
+ * console.log(options.refraction);
49
+ * ```
50
+ */
51
+ export interface HorizontalOptions {
52
+ /** `"J2000"` (the default) when the position is a catalogue position, which is turned to the equinox of the date first; `"date"` when it is already the mean equator and equinox of the moment (planets are, from an almanac). */
53
+ equinox?: "J2000" | "date";
54
+ /** True to lift the altitude by the air's bending of light (Saemundsson's formula, Meeus 16.4): about 0.48 degrees at the horizon, 0.02 at 45 degrees up. Default false, the geometric altitude. Not applied below -1 degree, where the formula stops being true. */
55
+ refraction?: boolean;
56
+ /** Air pressure in hectopascals for the refraction; default 1010 (a standard sea-level day). Only used with `refraction`. */
57
+ pressureHpa?: number;
58
+ /** Air temperature in degrees Celsius for the refraction; default 10. Only used with `refraction`. */
59
+ temperatureC?: number;
60
+ }
61
+ /**
62
+ * The spin of another planet, as the IAU WGCCRE gives it: where its north pole points, and how far its prime meridian has turned.
63
+ *
64
+ * The pole is a right ascension and declination in the ICRF (the same J2000 frame as the star positions). The prime meridian is `W = w0 + rate * d`, with `d` the days since J2000.0 (2000 January 1.5): `w0` is W at J2000 in degrees and `rate` the degrees a day (negative for a planet that turns backwards, such as Venus). W is the angle, east, from the point where the planet's equator crosses the ICRF equator going north (the right ascension of the pole plus 90 degrees) to its prime meridian.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * import type { PlanetSpin } from "@johnmorrisdotca/hoshi-stars/sky";
69
+ *
70
+ * // Mars, IAU 2015 values at J2000 (the slow drift of the pole is left out)
71
+ * const mars: PlanetSpin = { pole: { rightAscension: 317.269202, declination: 54.432516 }, primeMeridian: { w0: 176.049863, rate: 350.891982443297 } };
72
+ * console.log(mars.pole.declination);
73
+ * ```
74
+ */
75
+ export interface PlanetSpin {
76
+ /** The direction of the planet's north pole at J2000, in degrees. */
77
+ pole: SkyPoint;
78
+ /** The prime meridian: `w0` degrees at J2000 and `rate` degrees a day. */
79
+ primeMeridian: {
80
+ w0: number;
81
+ rate: number;
82
+ };
83
+ }
84
+ /**
85
+ * The Greenwich mean sidereal time: how far the Earth has turned against the stars, as the right ascension on the meridian of Greenwich, in degrees (Meeus 12.4, the IAU 1982 expression). It uses UT, which is UTC to under a second, and is the mean time, without the nutation term (at most about 1.1 seconds of time, 17 arc-seconds).
86
+ *
87
+ * @param when - The moment, a `Date` or milliseconds since 1970.
88
+ * @returns Degrees, 0 up to but not including 360. Divide by 15 for hours.
89
+ * @throws TypeError if `when` is not a Date or a number, RangeError if it is an invalid Date or not finite.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import { greenwichSiderealTime } from "@johnmorrisdotca/hoshi-stars/sky";
94
+ *
95
+ * // Meeus example 12.a: 1987 April 10 at 0h UT is 13h10m46.3668s, 197.693195 degrees
96
+ * console.log(greenwichSiderealTime(Date.UTC(1987, 3, 10)).toFixed(6));
97
+ * ```
98
+ */
99
+ export declare function greenwichSiderealTime(when: When): number;
100
+ /**
101
+ * The local mean sidereal time: the right ascension that is on your meridian, due south or north of you, in degrees. A star with this right ascension is crossing the meridian.
102
+ *
103
+ * @param when - The moment, a `Date` or milliseconds since 1970.
104
+ * @param longitude - Degrees east of Greenwich (west is negative). Any finite number.
105
+ * @returns Degrees, 0 up to but not including 360.
106
+ * @throws TypeError if `when` is not a Date or a number, RangeError if it is invalid or `longitude` is not finite.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * import { localSiderealTime } from "@johnmorrisdotca/hoshi-stars/sky";
111
+ *
112
+ * // Greenwich's 197.693195 degrees at 0h UT, 1987 April 10, and Washington is 77.07 degrees west of it
113
+ * console.log(localSiderealTime(Date.UTC(1987, 3, 10), -77.0656).toFixed(3)); // 120.628
114
+ * ```
115
+ */
116
+ export declare function localSiderealTime(when: When, longitude: number): number;
117
+ /**
118
+ * A J2000 position carried to the mean equator and equinox of a date: the precession of the Earth's axis (Meeus chapter 21, the rigorous method with the IAU 1976 constants, good for a few centuries either side of 2000 to far under an arc-second). It leaves out the star's own proper motion (which `starPosition` adds), nutation and aberration, together under a minute of arc.
119
+ *
120
+ * @param position - A right ascension and declination in degrees at J2000.
121
+ * @param when - The date to carry it to.
122
+ * @returns The right ascension (0 up to but not including 360) and declination in degrees.
123
+ * @throws TypeError or RangeError, naming the argument, if `position` or `when` is not valid.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import { precess } from "@johnmorrisdotca/hoshi-stars/sky";
128
+ *
129
+ * // Meeus example 21.b: theta Persei, carried to 2028 November 13.19 (about 15 minutes of arc east in right ascension)
130
+ * const there = precess({ rightAscension: 41.054063, declination: 49.227750 }, Date.UTC(2028, 10, 13, 4, 33, 36));
131
+ * console.log(there.rightAscension.toFixed(3), there.declination.toFixed(3)); // 41.547 49.348
132
+ * ```
133
+ */
134
+ export declare function precess(position: SkyPoint, when: When): SkyPoint;
135
+ /**
136
+ * Where a star (or any J2000 position) stands in the sky of a place on Earth at a moment: its altitude above the horizon and azimuth from north through east. The position is carried from J2000 to the equinox of the date (`precess`), the hour angle comes from the local mean sidereal time, and the horizon formulas are Meeus's (chapter 13). The result is geometric (no refraction) unless you ask for it. UTC stands for UT; nutation, aberration and parallax are left out, so it is good to about an arc-minute, far finer than the eye. The star's own proper motion is not added: pass `starPosition(star, when)` if you want it.
137
+ *
138
+ * @param position - A right ascension and declination in degrees, J2000 (the catalogue's frame), or of the date with `equinox: "date"`.
139
+ * @param observer - Latitude north and longitude east in degrees, and optionally `heightM`.
140
+ * @param when - The moment, a `Date` or milliseconds since 1970.
141
+ * @param options - `equinox`, and `refraction` with `pressureHpa` and `temperatureC`.
142
+ * @returns `{ altitude, azimuth }` in degrees.
143
+ * @throws TypeError or RangeError, naming the argument and the value, for a position, observer, moment or option that is not valid (a latitude or declination outside -90 to 90, a number that is not finite, a `pressureHpa` of 0 or less).
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * import { starByName, starPosition } from "@johnmorrisdotca/hoshi-stars";
148
+ * import { horizontal } from "@johnmorrisdotca/hoshi-stars/sky";
149
+ *
150
+ * const polaris = starPosition(starByName("Polaris")!);
151
+ * const view = horizontal(polaris, { latitude: 49.28, longitude: -123.12 }, new Date("2026-11-08T06:00:00Z"));
152
+ * console.log(view.altitude.toFixed(1)); // 49.8: within a degree of the latitude, 49.28
153
+ * ```
154
+ */
155
+ export declare function horizontal(position: SkyPoint, observer: Observer, when: When, options?: HorizontalOptions): Horizontal;
156
+ /**
157
+ * Whether a position is above the horizon of a place at a moment (altitude greater than 0). With `refraction: true` it counts the bending of the light, so a star can be seen a little below the geometric horizon.
158
+ *
159
+ * @param position - A right ascension and declination in degrees, J2000 or (with `equinox: "date"`) of the date.
160
+ * @param observer - Latitude north and longitude east in degrees.
161
+ * @param when - The moment, a `Date` or milliseconds since 1970.
162
+ * @param options - The same as `horizontal`.
163
+ * @returns True when it is up.
164
+ * @throws TypeError or RangeError, as `horizontal` does.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * import { starByName, starPosition } from "@johnmorrisdotca/hoshi-stars";
169
+ * import { isAboveHorizon } from "@johnmorrisdotca/hoshi-stars/sky";
170
+ *
171
+ * // Polaris never sets for anyone in the northern hemisphere
172
+ * const polaris = starPosition(starByName("Polaris")!);
173
+ * console.log(isAboveHorizon(polaris, { latitude: 49.28, longitude: -123.12 }, Date.now()));
174
+ * ```
175
+ */
176
+ export declare function isAboveHorizon(position: SkyPoint, observer: Observer, when: When, options?: HorizontalOptions): boolean;
177
+ /**
178
+ * Where a star stands in the sky of a place on ANOTHER planet: the sky turns about that planet's pole (the IAU WGCCRE pole and prime meridian, passed in as `spin`), so the star is rotated into the planet's own equatorial frame, its hour angle is the prime meridian plus the observer's east longitude minus its right ascension there, and the horizon formulas are the same as for Earth.
179
+ *
180
+ * The stars look the same from every planet: the nearest star, Proxima Centauri, shifts only about 40 arc-seconds seen from Pluto (annual parallax 0.77 arc-seconds times Pluto's 49 AU at its farthest), and a typical star far less, so a star's J2000 position serves anywhere in the solar system. Only the pole and the turning change.
181
+ *
182
+ * Limits: the latitude is used as the angle from the planet's equator on a sphere (the difference between planetographic and planetocentric latitude, up to a fifth of a degree on Mars, is ignored); the pole's slow drift is the caller's to add in if wanted (`spin.pole` is read as given); there is no atmosphere, and no precession of the pole is applied. `when` is UTC, like every moment in the package: it is turned into Terrestrial Time inside, the scale the IAU rates are in (69.184 s ahead of UTC since 2017, by the leap-second table back to 1972 and by Espenak and Meeus's delta T before it; a leap second announced after 2017 is not known until the table gets its row). TDB differs from TT by under 2 milliseconds, which is ignored. Without the conversion Mars would be turned 0.28 degrees out.
183
+ *
184
+ * @param position - A right ascension and declination in degrees at J2000.
185
+ * @param spin - The planet's `pole` (right ascension and declination, degrees) and `primeMeridian` (`w0` degrees at J2000 and `rate` degrees a day).
186
+ * @param observer - Latitude north and longitude east on that planet, in degrees (north being the side of the pole).
187
+ * @param when - The moment, a `Date` or milliseconds since 1970.
188
+ * @returns `{ altitude, azimuth }` in degrees, the azimuth from north through east.
189
+ * @throws TypeError or RangeError, naming the argument and the value, for a position, spin, observer or moment that is not valid.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * import { starByName, starPosition } from "@johnmorrisdotca/hoshi-stars";
194
+ * import { horizontalFrom } from "@johnmorrisdotca/hoshi-stars/sky";
195
+ *
196
+ * // Mars, IAU 2015 values: its north celestial pole is only 10 degrees from Deneb, so from the pole Deneb stands almost overhead
197
+ * const mars = { pole: { rightAscension: 317.269202, declination: 54.432516 }, primeMeridian: { w0: 176.049863, rate: 350.891982443297 } };
198
+ * const deneb = starPosition(starByName("Deneb")!);
199
+ * console.log(horizontalFrom(deneb, mars, { latitude: 90, longitude: 0 }, Date.UTC(2026, 10, 8)).altitude.toFixed(1)); // 79.8
200
+ * ```
201
+ */
202
+ export declare function horizontalFrom(position: SkyPoint, spin: PlanetSpin, observer: Observer, when: When): Horizontal;