celestial-chart 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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,105 @@
1
+ # Changelog
2
+
3
+ ## 0.8.0 — first release of the fork
4
+
5
+ Forked from [d3-celestial](https://github.com/ofrohn/d3-celestial) `0.7.35`
6
+ (commit `7e720a3`, 2022-07-05).
7
+
8
+ Every change below is measured against the pinned upstream build: 67 projections
9
+ × 4 rotations × 413 sky points = 110 684 points, maximum difference **0.000 px**.
10
+
11
+ ### Astronomical fixes
12
+
13
+ - **[#148](https://github.com/ofrohn/d3-celestial/issues/148)** `horizontal.inverse()`
14
+ was missing a sign correction. `acos` returns 0–180°, so half the sky above the
15
+ horizon — exactly the half where `sin(azimuth) > 0` — came back wrong, by up to
16
+ 171°. The forward direction already handled the ambiguity. Round-trip error:
17
+ 50% of points → 0%.
18
+ - **[#130](https://github.com/ofrohn/d3-celestial/issues/130)** *Wrong moon phase?*
19
+ The phase computation is correct (verified against seven eclipse-anchored
20
+ syzygies). Two other defects produced the reported symptom:
21
+ - `moon.js` added Schlyter's **ecliptic** perturbation terms directly to right
22
+ ascension and declination, where `elements()` already accounts for them.
23
+ Meeus 13.a: RA error 1.50° → 0.0004°.
24
+ - The terminator ellipse used `1.6 · |ph − 0.5|` where the geometry is
25
+ `|2·ph − 1|`, so a full moon was drawn 19% narrow — as a gibbous.
26
+ - **[#157](https://github.com/ofrohn/d3-celestial/issues/157)** The guard written
27
+ for the 180° centre change (`if (d > 3.14)`) was unreachable: `Round(d, 2)`
28
+ cannot exceed 3.14. Antipodal endpoints then went through `geoInterpolate`,
29
+ which divides by `sin(d) = 1.2e-16`. Measured angular error up to 139°.
30
+ - `Celestial.ha()` normalised with `if (ha < 180)` instead of `if (ha < 0)`,
31
+ returning hour angles such as 460°.
32
+ - `Trig.normalize` / `normalize0` did not normalise inputs below −2π (−3π).
33
+ Latent: the Moon's position stays sane from 1700 to 2300 either way.
34
+
35
+ ### Rendering fixes
36
+
37
+ - Star colours: `d3.scaleQuantize` no longer accepts a descending domain, so
38
+ after the D3 upgrade **every star rendered red**. The scale is now built
39
+ ascending with a reversed range.
40
+ - Milky Way: d3-geo filled the **complement** of the outline at some
41
+ orientations — the map greyed out and the Milky Way showed black. Detected by
42
+ asking the rendered path whether it contains the galactic pole, which is known
43
+ to lie outside every Milky Way contour. Verified across 44 orientations.
44
+ - Window resize reset the user's zoom: the guard in `resize(set)` never fired,
45
+ because in D3 v7 the listener's first argument is the event object.
46
+ - SVG export wrote `Infinity` coordinates for mercator (the 179.95° background
47
+ circle includes the poles); browsers discard such paths.
48
+ - The map failed to render when given neither a container element nor a width —
49
+ `getWidth()` called `window.getBoundingClientRect()`, which does not exist.
50
+
51
+ ### Projections
52
+
53
+ - `hatano` and `wagner7` restored: d3-geo-projection v4 no longer exports their
54
+ raw functions.
55
+ - `healpix` restored to the v3 normalisation: v4 rescales x and y differently,
56
+ so the configured `scale`/`ratio` would produce a wrongly proportioned map.
57
+ - `twoPointEquidistant`: v4's raw doubles its argument; the configured values are
58
+ calibrated for v3.
59
+ - `wiechel`: reflecting the projected x is **not** equivalent to flipping the
60
+ longitude for this projection, because the sign of λ also matters inside
61
+ `atan2`. The v3 wrapping is kept.
62
+ - `cassini` and `quincuncial` remain unavailable — they are absent from the
63
+ shipped upstream build as well.
64
+
65
+ ### Architecture
66
+
67
+ - D3 v7, ES modules, tree-shaken and bundled. Four outputs: IIFE (global
68
+ `Celestial`, unchanged usage), minified IIFE, ESM and CJS.
69
+ - Class-based instances (`SkyMap`), so several maps can share a page — each with
70
+ its own configuration, projection, container and settings form
71
+ ([#96](https://github.com/ofrohn/d3-celestial/issues/96),
72
+ [#131](https://github.com/ofrohn/d3-celestial/issues/131)).
73
+ - Repeated `Celestial.display()` calls no longer append a duplicate settings form
74
+ (six calls produced 469 fields instead of 67, sharing ids).
75
+ - TypeScript definitions, with a test that keeps them from drifting from the code.
76
+ - The spherical zoom plugin now uses [`versor`](https://github.com/d3/versor)
77
+ (ISC) for the quaternion mathematics instead of a vendored copy.
78
+
79
+ ### Privacy
80
+
81
+ - **No API key is shipped any more.** Upstream's defaults carried the author's
82
+ TimeZoneDB account id, and `settimezone: true` was also a default — so any page
83
+ embedding the library sent its visitors' coordinates to a third party, unasked,
84
+ on a quota shared with every other d3-celestial site, over plain HTTP whenever
85
+ the page itself was served over HTTP. A key in a client-side bundle cannot be
86
+ kept secret, so the fix is no key rather than a different one.
87
+ - New `timezoneResolver(lat, lon, whenSeconds) => number | Promise<number>`
88
+ config option: resolve the offset from your own service. It takes precedence
89
+ over `timezoneid`, which still works with a key of your own.
90
+ - A test now fails the build if a credential-looking literal reappears in `src/`.
91
+
92
+ ### Breaking changes
93
+
94
+ Behaviour that differs from upstream — all of them consequences of the fixes above:
95
+
96
+ - Moon right ascension and declination move by up to 1.50°; the terminator is wider.
97
+ - `horizontal.inverse()` returns the correct hemisphere.
98
+ - `Celestial.ha()` returns `[0, 360)`.
99
+ - SVG export clips paths to the output size.
100
+ - `Celestial.display()` additionally returns the created instance.
101
+ - With no `timezoneid` and no `timezoneResolver`, the UTC offset of a position is
102
+ estimated from longitude instead of being looked up remotely. Configure one of
103
+ the two to get exact offsets back.
104
+
105
+ A global `d3` is no longer required, or used, by the bundle.
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015, Olaf Frohn
2
+ Copyright (c) 2026, Fekete László
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10
+
11
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/NOTICE.md ADDED
@@ -0,0 +1,41 @@
1
+ # Notices and attributions
2
+
3
+ `celestial-chart` is a fork of [d3-celestial](https://github.com/ofrohn/d3-celestial)
4
+ by Olaf Frohn, distributed under the BSD 3-Clause License. See `LICENSE` for the
5
+ full text, which covers both the original work and this fork.
6
+
7
+ Per clause 3 of that license, the name of the original copyright holder is used
8
+ here **only to identify the origin of the code** — not to endorse or promote this
9
+ fork. This project is not affiliated with, nor endorsed by, Olaf Frohn.
10
+
11
+ Likewise, this package is not an official D3 module and is not affiliated with
12
+ the D3 project.
13
+
14
+ ## Bundled dependencies
15
+
16
+ The distributed bundles (`build/celestial.js`, `.min.js`, `.mjs`, `.cjs`) include
17
+ the following packages, all © Mike Bostock, ISC License:
18
+
19
+ d3-dispatch d3-fetch d3-geo d3-geo-projection
20
+ d3-interpolate d3-scale d3-selection d3-shape
21
+ d3-time-format d3-transition d3-zoom versor
22
+
23
+ ISC License text: https://opensource.org/licenses/ISC
24
+
25
+ ## Algorithm credits
26
+
27
+ **Spherical drag-to-rotate** (`lib/geo-zoom.js`) implements the approach of
28
+ Jason Davies' `d3.geo.zoom` plugin (2014, http://www.jasondavies.com/maps/rotate/).
29
+ The quaternion mathematics is **not** copied from that plugin: it comes from the
30
+ `versor` package (Mike Bostock, ISC), which provides the same computation under
31
+ clear terms. The D3 v7 glue code is original to this fork.
32
+
33
+ **Astronomical algorithms** in `src/kepler.js` and `src/moon.js` follow Paul
34
+ Schlyter's *How to compute planetary positions* and Jean Meeus' *Astronomical
35
+ Algorithms*; both are inherited from the upstream project.
36
+
37
+ ## Data files
38
+
39
+ The star, deep-sky and Milky Way data under `harness/data/` is a minimal subset
40
+ copied from the upstream repository for reproducible testing. The full data sets,
41
+ their sources and licensing are documented upstream.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # celestial-chart
2
+
3
+ Interactive celestial map for the browser: stars, constellations, deep-sky objects,
4
+ the Milky Way and planets, in 67 map projections, rendered to canvas with SVG export.
5
+
6
+ A modernised fork of [d3-celestial](https://github.com/ofrohn/d3-celestial) by
7
+ Olaf Frohn, whose last release was in 2022 and which is pinned to D3 v3.
8
+
9
+ **Live demo: [celestial.blackit.hu](https://celestial.blackit.hu)** — pick a
10
+ projection, drag to rotate, scroll to zoom.
11
+
12
+ ```bash
13
+ npm install celestial-chart
14
+ ```
15
+
16
+ ```js
17
+ import Celestial from "celestial-chart";
18
+
19
+ Celestial.display({
20
+ container: "celestial-map",
21
+ projection: "aitoff",
22
+ stars: { limit: 6 },
23
+ constellations: { names: true, lines: true }
24
+ });
25
+ ```
26
+
27
+ Or a single script tag — **D3 is bundled, no separate `<script>` needed**:
28
+
29
+ ```html
30
+ <link rel="stylesheet" href="node_modules/celestial-chart/celestial.css">
31
+ <div id="celestial-map"></div>
32
+ <script src="node_modules/celestial-chart/build/celestial.min.js"></script>
33
+ ```
34
+
35
+ ## What this fork changes
36
+
37
+ **It runs on current D3.** Upstream is pinned to D3 v3 (2016). This fork uses
38
+ D3 v7 as ES modules, bundled in — which resolves upstream
39
+ [#147](https://github.com/ofrohn/d3-celestial/issues/147) (D3 upgrade),
40
+ [#141](https://github.com/ofrohn/d3-celestial/issues/141) (ES modules),
41
+ [#134](https://github.com/ofrohn/d3-celestial/issues/134) (`d3 is not defined`),
42
+ [#115](https://github.com/ofrohn/d3-celestial/issues/115) (webpack),
43
+ [#86](https://github.com/ofrohn/d3-celestial/issues/86) (React) and
44
+ [#81](https://github.com/ofrohn/d3-celestial/issues/81) (Node).
45
+
46
+ One file instead of three, and smaller than the original despite a much newer D3:
47
+
48
+ | | files | minified |
49
+ |---|---:|---:|
50
+ | upstream (d3 + plugin + celestial) | 3 | 316 KB |
51
+ | celestial-chart | **1** | **295 KB** |
52
+
53
+ **Several maps on one page.** Upstream kept global state, so a page could hold
54
+ one map ([#96](https://github.com/ofrohn/d3-celestial/issues/96),
55
+ [#131](https://github.com/ofrohn/d3-celestial/issues/131)). Each instance now
56
+ owns its state — including its own settings form:
57
+
58
+ ```js
59
+ import { SkyMap } from "celestial-chart";
60
+
61
+ const a = new SkyMap({ container: "map-a", projection: "orthographic" }, { standalone: true });
62
+ const b = new SkyMap({ container: "map-b", projection: "mollweide" }, { standalone: true });
63
+ ```
64
+
65
+ **TypeScript types ship with the package.** Projection and coordinate-system
66
+ names are union types, so the editor offers all 69.
67
+
68
+ **Astronomical fixes.**
69
+
70
+ | | |
71
+ |---|---|
72
+ | [#148](https://github.com/ofrohn/d3-celestial/issues/148) | `horizontal.inverse()` lost the sign: **half** the sky above the horizon came back wrong, by up to 171°. |
73
+ | [#130](https://github.com/ofrohn/d3-celestial/issues/130) | The phase computation was fine; two other things were not. `moon.js` added ecliptic perturbations directly to right ascension — a **1.5° error**, three lunar diameters (Meeus 13.a). And the terminator ellipse used a factor of 1.6 instead of 2, so a **full moon was drawn gibbous**. |
74
+ | [#157](https://github.com/ofrohn/d3-celestial/issues/157) | The guard written for the 180° turn was unreachable (`Round(d,2)` never exceeds 3.14), so antipodal centre changes went through a degenerate great-circle interpolation — measured error up to 139°. |
75
+
76
+ Plus, found while testing: every star rendered red after the D3 upgrade
77
+ (`scaleQuantize` no longer accepts a descending domain); window resizing reset
78
+ the user's zoom; the settings form was appended again on every `display()` call;
79
+ `Celestial.ha()` returned hour angles outside any sane range; and the map failed
80
+ to render at all when given neither a container element nor a width.
81
+
82
+ ## Time zones, and the key that is not here
83
+
84
+ The settings form shows the observer's local time, which means turning a
85
+ position into a UTC offset. A browser cannot do that for an arbitrary point on
86
+ Earth — only for the viewer's own zone — so it takes an outside source.
87
+
88
+ Upstream shipped the author's TimeZoneDB account id as a default, with
89
+ `settimezone: true` also being a default. Every page embedding the library
90
+ therefore sent its visitors' coordinates to a third party, unasked, on a quota
91
+ shared with every other d3-celestial site — and over plain HTTP whenever the
92
+ page itself was served over HTTP.
93
+
94
+ **This fork ships no key and no endpoint.** With nothing configured, no request
95
+ is made and the offset is estimated from longitude (15° per hour) — the fallback
96
+ the code always had for failed lookups. Accurate enough for most places, wrong
97
+ by up to an hour or two where zones follow politics rather than meridians.
98
+
99
+ To get exact offsets, configure one of these:
100
+
101
+ ```js
102
+ // Your own service — nothing leaves your infrastructure.
103
+ Celestial.display({
104
+ timezoneResolver: (lat, lon, when) =>
105
+ fetch(`https://example.org/timezone?lat=${lat}&lon=${lon}&t=${when}`)
106
+ .then(r => r.json())
107
+ .then(j => j.offsetMinutes)
108
+ });
109
+
110
+ // Or the upstream route, with a key of your own.
111
+ Celestial.display({ timezoneid: "YOUR_OWN_TIMEZONEDB_KEY" });
112
+ ```
113
+
114
+ `timezoneResolver` wins over `timezoneid`; if it rejects, the longitude estimate
115
+ takes over. A key in a client-side bundle is readable by anyone who loads the
116
+ page, so treat `timezoneid` as public, and prefer a resolver you control.
117
+
118
+ ## Backwards compatibility
119
+
120
+ `Celestial.display(config)` behaves as before, including how successive calls
121
+ accumulate settings. It now also returns the instance it created.
122
+
123
+ Behaviour that intentionally differs — all of them bug fixes:
124
+
125
+ - Moon right ascension and declination move by up to 1.5°; the terminator is drawn wider.
126
+ - `horizontal.inverse()` returns the correct hemisphere.
127
+ - `Celestial.ha()` returns `[0, 360)`.
128
+ - SVG export clips paths to the output size (mercator produced `Infinity` coordinates, which browsers discard).
129
+ - `Trig.normalize` / `normalize0` normalise inputs below −2π correctly.
130
+
131
+ ## How it is verified
132
+
133
+ Projection is a deterministic function — `(RA, Dec, projection, rotation) → (x, y)` —
134
+ so it can be pinned. `harness/` holds the output of the **pinned upstream v3 build**
135
+ and compares the fork against it:
136
+
137
+ ```
138
+ 67 projections × 4 rotations × 413 sky points = 110 684 measured points
139
+ maximum difference: 0.000 px
140
+ ```
141
+
142
+ Bit-identical, including the clipping state of every point. The harness checks
143
+ itself too: it fails if two projections — or two rotations — produce the same
144
+ output, because a net that measures nothing passes everything.
145
+
146
+ ```bash
147
+ npm run verify # build + 71 unit tests + types + 28 browser assertions, ~2 min
148
+ ```
149
+
150
+ The browser run regenerates both references, compares them, captures 12 screenshots
151
+ with a pixel diff, and drives the real UI (zoom, drag-rotate, projection switching,
152
+ forms, SVG export, two independent maps). It runs in CI on every push.
153
+
154
+ ## Trying it locally
155
+
156
+ The data files are not in the npm package (they are large and they are the
157
+ upstream project's catalogues), so the demos load them from `harness/data/`.
158
+ They need a real HTTP server — in D3 v7 `d3.json` uses `fetch`, which the
159
+ browser refuses on `file://`.
160
+
161
+ ```bash
162
+ npm run serve # then open http://127.0.0.1:8877/demo/full.html
163
+ ```
164
+
165
+ | page | what it shows |
166
+ |---|---|
167
+ | [`demo/full.html`](demo/full.html) | the whole interface: settings form, controls, location, SVG export |
168
+ | [`demo/module.html`](demo/module.html) | the same as an ES module import |
169
+ | [`demo/two-maps.html`](demo/two-maps.html) | two independent maps on one page |
170
+ | [`demo/two-forms.html`](demo/two-forms.html) | two maps, each with its own settings form |
171
+
172
+ The landing page behind the live demo lives in its own repository,
173
+ [celestial-demo](https://github.com/Fekete85/celestial-demo) — it consumes this
174
+ package as a dependency, so it is also a standing check that installing and
175
+ using the published library actually works.
176
+
177
+ ## Documentation
178
+
179
+ The full configuration is documented in the
180
+ [upstream readme](https://github.com/ofrohn/d3-celestial#configuration) — the
181
+ option names are unchanged. Type definitions in `types/celestial.d.ts` list them
182
+ all, and a test keeps them from drifting from the code.
183
+
184
+ The migration itself is written up in [`docs/`](docs/) — what was measured,
185
+ what broke, and why.
186
+
187
+ ## License
188
+
189
+ BSD 3-Clause, inherited from d3-celestial. See [`LICENSE`](LICENSE) and
190
+ [`NOTICE.md`](NOTICE.md).
191
+
192
+ This project is not affiliated with, nor endorsed by, Olaf Frohn or the D3 project.