ether-to-astro 1.0.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/.env.example +13 -0
- package/.github/pull_request_template.md +16 -0
- package/.github/workflows/release.yml +35 -0
- package/.github/workflows/test.yml +32 -0
- package/AGENTS.md +99 -0
- package/LICENSE +18 -0
- package/NOTICE.md +45 -0
- package/README.md +301 -0
- package/SETUP.md +70 -0
- package/TESTING_SUMMARY.md +238 -0
- package/TEST_SUITE_STATUS.md +218 -0
- package/biome.json +48 -0
- package/dist/astro-service.d.ts +98 -0
- package/dist/astro-service.js +496 -0
- package/dist/chart-types.d.ts +52 -0
- package/dist/chart-types.js +51 -0
- package/dist/charts.d.ts +125 -0
- package/dist/charts.js +324 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +472 -0
- package/dist/constants.d.ts +81 -0
- package/dist/constants.js +76 -0
- package/dist/eclipses.d.ts +85 -0
- package/dist/eclipses.js +184 -0
- package/dist/ephemeris.d.ts +120 -0
- package/dist/ephemeris.js +379 -0
- package/dist/formatter.d.ts +2 -0
- package/dist/formatter.js +22 -0
- package/dist/houses.d.ts +82 -0
- package/dist/houses.js +169 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +150 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +31 -0
- package/dist/logger.d.ts +25 -0
- package/dist/logger.js +73 -0
- package/dist/profile-store.d.ts +48 -0
- package/dist/profile-store.js +156 -0
- package/dist/riseset.d.ts +82 -0
- package/dist/riseset.js +185 -0
- package/dist/storage.d.ts +10 -0
- package/dist/storage.js +40 -0
- package/dist/time-utils.d.ts +68 -0
- package/dist/time-utils.js +136 -0
- package/dist/tool-registry.d.ts +35 -0
- package/dist/tool-registry.js +307 -0
- package/dist/tool-result.d.ts +175 -0
- package/dist/tool-result.js +188 -0
- package/dist/transits.d.ts +108 -0
- package/dist/transits.js +263 -0
- package/dist/types.d.ts +450 -0
- package/dist/types.js +161 -0
- package/example-usage.md +131 -0
- package/natal-chart.json +187 -0
- package/package.json +61 -0
- package/scripts/download-ephemeris.js +115 -0
- package/setup.sh +21 -0
- package/src/astro-service.ts +710 -0
- package/src/chart-types.ts +125 -0
- package/src/charts.ts +399 -0
- package/src/cli.ts +694 -0
- package/src/constants.ts +89 -0
- package/src/eclipses.ts +226 -0
- package/src/ephemeris.ts +437 -0
- package/src/formatter.ts +25 -0
- package/src/houses.ts +202 -0
- package/src/index.ts +170 -0
- package/src/loader.ts +36 -0
- package/src/logger.ts +104 -0
- package/src/profile-store.ts +285 -0
- package/src/riseset.ts +229 -0
- package/src/time-utils.ts +167 -0
- package/src/tool-registry.ts +357 -0
- package/src/tool-result.ts +283 -0
- package/src/transits.ts +352 -0
- package/src/types.ts +547 -0
- package/tests/README.md +173 -0
- package/tests/TESTING_STRATEGY.md +178 -0
- package/tests/fixtures/bowen-yang-chart.ts +69 -0
- package/tests/fixtures/calculate-expected.ts +81 -0
- package/tests/fixtures/expected-results.ts +117 -0
- package/tests/fixtures/generate-expected-simple.ts +94 -0
- package/tests/helpers/date-fixtures.ts +15 -0
- package/tests/helpers/ephem.ts +11 -0
- package/tests/helpers/temp.ts +9 -0
- package/tests/setup.ts +11 -0
- package/tests/unit/astro-service.test.ts +323 -0
- package/tests/unit/chart-types.test.ts +18 -0
- package/tests/unit/charts-errors.test.ts +42 -0
- package/tests/unit/charts.test.ts +157 -0
- package/tests/unit/cli-commands.test.ts +82 -0
- package/tests/unit/cli-profiles.test.ts +128 -0
- package/tests/unit/cli.test.ts +191 -0
- package/tests/unit/constants.test.ts +26 -0
- package/tests/unit/correctness-critical.test.ts +408 -0
- package/tests/unit/eclipses.test.ts +108 -0
- package/tests/unit/ephemeris.test.ts +213 -0
- package/tests/unit/error-handling.test.ts +116 -0
- package/tests/unit/formatter.test.ts +29 -0
- package/tests/unit/houses-errors.test.ts +27 -0
- package/tests/unit/houses-validation.test.ts +164 -0
- package/tests/unit/houses.test.ts +205 -0
- package/tests/unit/profile-store.test.ts +163 -0
- package/tests/unit/real-user-charts.test.ts +148 -0
- package/tests/unit/riseset.test.ts +106 -0
- package/tests/unit/solver-edges.test.ts +197 -0
- package/tests/unit/time-utils-temporal.test.ts +303 -0
- package/tests/unit/time-utils.test.ts +173 -0
- package/tests/unit/tool-registry.test.ts +222 -0
- package/tests/unit/tool-result.test.ts +45 -0
- package/tests/unit/transit-correctness.test.ts +78 -0
- package/tests/unit/transits.test.ts +238 -0
- package/tests/validation/README.md +32 -0
- package/tests/validation/adapters/astrolog.ts +306 -0
- package/tests/validation/adapters/internal.ts +184 -0
- package/tests/validation/compare/eclipses.ts +47 -0
- package/tests/validation/compare/houses.ts +76 -0
- package/tests/validation/compare/positions.ts +104 -0
- package/tests/validation/compare/riseSet.ts +48 -0
- package/tests/validation/compare/roots.ts +90 -0
- package/tests/validation/compare/transits.ts +69 -0
- package/tests/validation/fixtures/astrolog-parity/core.ts +194 -0
- package/tests/validation/fixtures/eclipses/core.ts +14 -0
- package/tests/validation/fixtures/houses/core.ts +47 -0
- package/tests/validation/fixtures/positions/core.ts +159 -0
- package/tests/validation/fixtures/rise-set/core.ts +20 -0
- package/tests/validation/fixtures/roots/core.ts +47 -0
- package/tests/validation/fixtures/transits/core.ts +61 -0
- package/tests/validation/fixtures/transits/dst.ts +21 -0
- package/tests/validation/oracle.spec.ts +129 -0
- package/tests/validation/utils/denseRootOracle.ts +269 -0
- package/tests/validation/utils/fixtureTypes.ts +146 -0
- package/tests/validation/utils/report.ts +60 -0
- package/tests/validation/utils/tolerances.ts +23 -0
- package/tests/validation/validation.spec.ts +836 -0
- package/tools/color-picker.html +388 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +31 -0
package/dist/charts.d.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { type ChartFormat, type ChartTheme } from './chart-types.js';
|
|
2
|
+
import type { EphemerisCalculator } from './ephemeris.js';
|
|
3
|
+
import type { HouseCalculator } from './houses.js';
|
|
4
|
+
import { type NatalChart } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Renderer for astrological charts using SVG
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Generates natal and transit charts as SVG images using the astrochart library.
|
|
10
|
+
* Converts SVG to PNG/WebP formats for output. Uses JSDOM to provide
|
|
11
|
+
* browser environment for the chart library.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ChartRenderer {
|
|
14
|
+
/** Ephemeris calculator instance */
|
|
15
|
+
private ephem;
|
|
16
|
+
/** House calculator instance */
|
|
17
|
+
private houseCalc;
|
|
18
|
+
/** Virtual DOM for chart rendering */
|
|
19
|
+
private dom;
|
|
20
|
+
/**
|
|
21
|
+
* Create a new chart renderer
|
|
22
|
+
*
|
|
23
|
+
* @param ephem - Initialized ephemeris calculator
|
|
24
|
+
* @param houseCalc - Initialized house calculator
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* Both calculators must be initialized before passing to the constructor.
|
|
28
|
+
* Sets up a virtual DOM environment for the astrochart library.
|
|
29
|
+
*/
|
|
30
|
+
constructor(ephem: EphemerisCalculator, houseCalc: HouseCalculator);
|
|
31
|
+
/**
|
|
32
|
+
* Setup global browser variables for astrochart library
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* The astrochart library expects browser globals like document and window.
|
|
36
|
+
* This method provides those from the virtual DOM. Required because
|
|
37
|
+
* astrochart was designed for browser environments.
|
|
38
|
+
*/
|
|
39
|
+
private setupGlobals;
|
|
40
|
+
/**
|
|
41
|
+
* Clear the chart container element
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* Removes any previous chart content to ensure clean rendering.
|
|
45
|
+
* Called before generating each new chart.
|
|
46
|
+
*/
|
|
47
|
+
private clearContainer;
|
|
48
|
+
/**
|
|
49
|
+
* Generate a natal chart visualization
|
|
50
|
+
*
|
|
51
|
+
* @param natalChart - Birth chart data with julianDay and houseSystem
|
|
52
|
+
* @param theme - Visual theme for the chart (default: 'light')
|
|
53
|
+
* @param format - Output format (default: 'svg')
|
|
54
|
+
* @returns Chart image as buffer or data URL
|
|
55
|
+
* @throws Error if natal chart is invalid or rendering fails
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
* Renders a complete natal chart with planets, houses, and aspects.
|
|
59
|
+
* Requires julianDay to be set in the natalChart.
|
|
60
|
+
*/
|
|
61
|
+
generateNatalChart(natalChart: NatalChart, theme?: ChartTheme, format?: ChartFormat): Promise<Buffer | string>;
|
|
62
|
+
/**
|
|
63
|
+
* Prepare chart data for astrochart library
|
|
64
|
+
*
|
|
65
|
+
* @param natalChart - Birth chart data
|
|
66
|
+
* @param transitDate - Optional date for transit calculations
|
|
67
|
+
* @returns Chart data in astrochart format
|
|
68
|
+
* @throws Error if julianDay is not set
|
|
69
|
+
*
|
|
70
|
+
* @remarks
|
|
71
|
+
* Converts internal chart format to astrochart's expected format.
|
|
72
|
+
* Includes planets, houses, and optionally transit positions.
|
|
73
|
+
*/
|
|
74
|
+
private prepareChartData;
|
|
75
|
+
/**
|
|
76
|
+
* Generate a transit chart visualization
|
|
77
|
+
*
|
|
78
|
+
* @param natalChart - Birth chart data with julianDay and houseSystem
|
|
79
|
+
* @param transitDate - Date for transit calculation
|
|
80
|
+
* @param theme - Visual theme for the chart (default: 'light')
|
|
81
|
+
* @param format - Output format (default: 'svg')
|
|
82
|
+
* @returns Chart image as buffer or data URL
|
|
83
|
+
* @throws Error if natal chart is invalid or rendering fails
|
|
84
|
+
*
|
|
85
|
+
* @remarks
|
|
86
|
+
* Shows both natal positions (inner wheel) and current transits (outer wheel).
|
|
87
|
+
* Requires julianDay to be set in the natalChart.
|
|
88
|
+
*/
|
|
89
|
+
generateTransitChart(natalChart: NatalChart, transitDate: Date, theme?: ChartTheme, format?: ChartFormat): Promise<Buffer | string>;
|
|
90
|
+
/**
|
|
91
|
+
* Map internal planet names to astrochart keys
|
|
92
|
+
*
|
|
93
|
+
* @param planetName - Internal planet name
|
|
94
|
+
* @returns Astrochart planet key or null if not supported
|
|
95
|
+
*
|
|
96
|
+
* @remarks
|
|
97
|
+
* Maps our planet names to the keys expected by astrochart.
|
|
98
|
+
* Uses mean node to avoid collision with true node.
|
|
99
|
+
*/
|
|
100
|
+
private getPlanetKey;
|
|
101
|
+
/**
|
|
102
|
+
* Extract SVG string from virtual DOM
|
|
103
|
+
*
|
|
104
|
+
* @returns SVG string
|
|
105
|
+
* @throws Error if no SVG element found
|
|
106
|
+
*
|
|
107
|
+
* @remarks
|
|
108
|
+
* Finds the SVG element in the virtual DOM and returns
|
|
109
|
+
* its outer HTML. Throws if no chart was rendered.
|
|
110
|
+
*/
|
|
111
|
+
private extractSVG;
|
|
112
|
+
/**
|
|
113
|
+
* Convert SVG to specified output format
|
|
114
|
+
*
|
|
115
|
+
* @param svg - SVG string or buffer
|
|
116
|
+
* @param format - Target output format
|
|
117
|
+
* @returns Converted image as buffer (png/webp) or data URL (svg)
|
|
118
|
+
* @throws Error if conversion fails
|
|
119
|
+
*
|
|
120
|
+
* @remarks
|
|
121
|
+
* Uses Sharp library for PNG/WebP conversion. SVG is returned
|
|
122
|
+
* as a data URL for direct use in web contexts.
|
|
123
|
+
*/
|
|
124
|
+
private convertToImage;
|
|
125
|
+
}
|
package/dist/charts.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import Chart from '@astrodraw/astrochart';
|
|
2
|
+
import { JSDOM } from 'jsdom';
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
import { getThemeSettings, } from './chart-types.js';
|
|
5
|
+
import { PLANETS } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Renderer for astrological charts using SVG
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Generates natal and transit charts as SVG images using the astrochart library.
|
|
11
|
+
* Converts SVG to PNG/WebP formats for output. Uses JSDOM to provide
|
|
12
|
+
* browser environment for the chart library.
|
|
13
|
+
*/
|
|
14
|
+
export class ChartRenderer {
|
|
15
|
+
/** Ephemeris calculator instance */
|
|
16
|
+
ephem;
|
|
17
|
+
/** House calculator instance */
|
|
18
|
+
houseCalc;
|
|
19
|
+
/** Virtual DOM for chart rendering */
|
|
20
|
+
dom;
|
|
21
|
+
/**
|
|
22
|
+
* Create a new chart renderer
|
|
23
|
+
*
|
|
24
|
+
* @param ephem - Initialized ephemeris calculator
|
|
25
|
+
* @param houseCalc - Initialized house calculator
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Both calculators must be initialized before passing to the constructor.
|
|
29
|
+
* Sets up a virtual DOM environment for the astrochart library.
|
|
30
|
+
*/
|
|
31
|
+
constructor(ephem, houseCalc) {
|
|
32
|
+
this.ephem = ephem;
|
|
33
|
+
this.houseCalc = houseCalc;
|
|
34
|
+
// Create virtual DOM
|
|
35
|
+
this.dom = new JSDOM('<!DOCTYPE html><html><body><div id="chart-container"></div></body></html>');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Setup global browser variables for astrochart library
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* The astrochart library expects browser globals like document and window.
|
|
42
|
+
* This method provides those from the virtual DOM. Required because
|
|
43
|
+
* astrochart was designed for browser environments.
|
|
44
|
+
*/
|
|
45
|
+
setupGlobals() {
|
|
46
|
+
// Set global document and window for astrochart
|
|
47
|
+
// Note: Required by astrochart library which expects browser globals
|
|
48
|
+
const g = global;
|
|
49
|
+
g.document = this.dom.window.document;
|
|
50
|
+
g.window = this.dom.window;
|
|
51
|
+
g.SVGElement = this.dom.window.SVGElement;
|
|
52
|
+
g.self = this.dom.window;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Clear the chart container element
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
* Removes any previous chart content to ensure clean rendering.
|
|
59
|
+
* Called before generating each new chart.
|
|
60
|
+
*/
|
|
61
|
+
clearContainer() {
|
|
62
|
+
const container = this.dom.window.document.getElementById('chart-container');
|
|
63
|
+
if (container) {
|
|
64
|
+
container.innerHTML = '';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generate a natal chart visualization
|
|
69
|
+
*
|
|
70
|
+
* @param natalChart - Birth chart data with julianDay and houseSystem
|
|
71
|
+
* @param theme - Visual theme for the chart (default: 'light')
|
|
72
|
+
* @param format - Output format (default: 'svg')
|
|
73
|
+
* @returns Chart image as buffer or data URL
|
|
74
|
+
* @throws Error if natal chart is invalid or rendering fails
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* Renders a complete natal chart with planets, houses, and aspects.
|
|
78
|
+
* Requires julianDay to be set in the natalChart.
|
|
79
|
+
*/
|
|
80
|
+
async generateNatalChart(natalChart, theme = 'light', format = 'svg') {
|
|
81
|
+
this.setupGlobals();
|
|
82
|
+
this.clearContainer();
|
|
83
|
+
// Require Julian Day (always set by set_natal_chart)
|
|
84
|
+
if (!natalChart.julianDay) {
|
|
85
|
+
throw new Error('Natal chart missing Julian Day - chart may be from old session. Please call set_natal_chart again.');
|
|
86
|
+
}
|
|
87
|
+
const jd = natalChart.julianDay;
|
|
88
|
+
// Get only renderable planet positions (those mapped in getPlanetKey)
|
|
89
|
+
const renderablePlanetIds = [
|
|
90
|
+
PLANETS.SUN,
|
|
91
|
+
PLANETS.MOON,
|
|
92
|
+
PLANETS.MERCURY,
|
|
93
|
+
PLANETS.VENUS,
|
|
94
|
+
PLANETS.MARS,
|
|
95
|
+
PLANETS.JUPITER,
|
|
96
|
+
PLANETS.SATURN,
|
|
97
|
+
PLANETS.URANUS,
|
|
98
|
+
PLANETS.NEPTUNE,
|
|
99
|
+
PLANETS.PLUTO,
|
|
100
|
+
PLANETS.CHIRON,
|
|
101
|
+
PLANETS.MEAN_NODE, // Use mean node, not true node
|
|
102
|
+
];
|
|
103
|
+
const positions = this.ephem.getAllPlanets(jd, renderablePlanetIds);
|
|
104
|
+
// Get houses using stored house system preference
|
|
105
|
+
const houseSystem = natalChart.houseSystem || 'P';
|
|
106
|
+
const houses = this.houseCalc.calculateHouses(jd, natalChart.location.latitude, natalChart.location.longitude, houseSystem);
|
|
107
|
+
// Convert to AstroChart format
|
|
108
|
+
const data = {
|
|
109
|
+
planets: {},
|
|
110
|
+
cusps: Array.from(houses.cusps).slice(1, 13), // Houses 1-12
|
|
111
|
+
};
|
|
112
|
+
// Map planet positions
|
|
113
|
+
positions.forEach((p) => {
|
|
114
|
+
const planetKey = this.getPlanetKey(p.planet);
|
|
115
|
+
if (planetKey) {
|
|
116
|
+
data.planets[planetKey] = [p.longitude];
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// Create chart with theme colors
|
|
120
|
+
const settings = {
|
|
121
|
+
SYMBOL_SCALE: 1.2,
|
|
122
|
+
STROKE_ONLY: false,
|
|
123
|
+
...getThemeSettings(theme, false),
|
|
124
|
+
};
|
|
125
|
+
const ChartClass = (Chart.default ||
|
|
126
|
+
Chart);
|
|
127
|
+
const chart = new ChartClass('chart-container', 800, 800, settings);
|
|
128
|
+
// Generate SVG
|
|
129
|
+
const radix = chart.radix(data);
|
|
130
|
+
radix.aspects();
|
|
131
|
+
const svgString = this.extractSVG();
|
|
132
|
+
// Convert to requested format
|
|
133
|
+
if (format === 'svg') {
|
|
134
|
+
return svgString;
|
|
135
|
+
}
|
|
136
|
+
return this.convertToImage(svgString, format, theme);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Prepare chart data for astrochart library
|
|
140
|
+
*
|
|
141
|
+
* @param natalChart - Birth chart data
|
|
142
|
+
* @param transitDate - Optional date for transit calculations
|
|
143
|
+
* @returns Chart data in astrochart format
|
|
144
|
+
* @throws Error if julianDay is not set
|
|
145
|
+
*
|
|
146
|
+
* @remarks
|
|
147
|
+
* Converts internal chart format to astrochart's expected format.
|
|
148
|
+
* Includes planets, houses, and optionally transit positions.
|
|
149
|
+
*/
|
|
150
|
+
prepareChartData(natalChart, transitDate) {
|
|
151
|
+
// Require Julian Day (always set by set_natal_chart)
|
|
152
|
+
if (!natalChart.julianDay) {
|
|
153
|
+
throw new Error('Natal chart missing Julian Day - chart may be from old session. Please call set_natal_chart again.');
|
|
154
|
+
}
|
|
155
|
+
const jd = natalChart.julianDay;
|
|
156
|
+
// Get houses using stored house system preference
|
|
157
|
+
const houseSystem = natalChart.houseSystem || 'P';
|
|
158
|
+
const houses = this.houseCalc.calculateHouses(jd, natalChart.location.latitude, natalChart.location.longitude, houseSystem);
|
|
159
|
+
// Convert to AstroChart format
|
|
160
|
+
const data = {
|
|
161
|
+
planets: {},
|
|
162
|
+
cusps: Array.from(houses.cusps).slice(1, 13), // Houses 1-12
|
|
163
|
+
};
|
|
164
|
+
// Define renderable planets for charts
|
|
165
|
+
const renderablePlanetIds = [
|
|
166
|
+
PLANETS.SUN,
|
|
167
|
+
PLANETS.MOON,
|
|
168
|
+
PLANETS.MERCURY,
|
|
169
|
+
PLANETS.VENUS,
|
|
170
|
+
PLANETS.MARS,
|
|
171
|
+
PLANETS.JUPITER,
|
|
172
|
+
PLANETS.SATURN,
|
|
173
|
+
PLANETS.URANUS,
|
|
174
|
+
PLANETS.NEPTUNE,
|
|
175
|
+
PLANETS.PLUTO,
|
|
176
|
+
PLANETS.CHIRON,
|
|
177
|
+
PLANETS.MEAN_NODE,
|
|
178
|
+
];
|
|
179
|
+
// Prefill natal planets from julianDay to ensure all renderable planets have positions
|
|
180
|
+
// This avoids fake 0° placeholders for planets missing from natalChart.planets
|
|
181
|
+
const natalPositions = this.ephem.getAllPlanets(jd, renderablePlanetIds);
|
|
182
|
+
natalPositions.forEach((p) => {
|
|
183
|
+
const planetKey = this.getPlanetKey(p.planet);
|
|
184
|
+
if (planetKey) {
|
|
185
|
+
data.planets[planetKey] = [p.longitude];
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
// Add transit planets if transit date provided
|
|
189
|
+
if (transitDate) {
|
|
190
|
+
const transitJD = this.ephem.dateToJulianDay(transitDate);
|
|
191
|
+
const transitPositions = this.ephem.getAllPlanets(transitJD, renderablePlanetIds);
|
|
192
|
+
transitPositions.forEach((p) => {
|
|
193
|
+
const planetKey = this.getPlanetKey(p.planet);
|
|
194
|
+
if (planetKey) {
|
|
195
|
+
// Add transit position as second element in array
|
|
196
|
+
// Natal position already exists from prefill above
|
|
197
|
+
const current = data.planets[planetKey];
|
|
198
|
+
if (current) {
|
|
199
|
+
data.planets[planetKey] = [current[0], p.longitude];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return data;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Generate a transit chart visualization
|
|
208
|
+
*
|
|
209
|
+
* @param natalChart - Birth chart data with julianDay and houseSystem
|
|
210
|
+
* @param transitDate - Date for transit calculation
|
|
211
|
+
* @param theme - Visual theme for the chart (default: 'light')
|
|
212
|
+
* @param format - Output format (default: 'svg')
|
|
213
|
+
* @returns Chart image as buffer or data URL
|
|
214
|
+
* @throws Error if natal chart is invalid or rendering fails
|
|
215
|
+
*
|
|
216
|
+
* @remarks
|
|
217
|
+
* Shows both natal positions (inner wheel) and current transits (outer wheel).
|
|
218
|
+
* Requires julianDay to be set in the natalChart.
|
|
219
|
+
*/
|
|
220
|
+
async generateTransitChart(natalChart, transitDate, theme = 'light', format = 'svg') {
|
|
221
|
+
this.setupGlobals();
|
|
222
|
+
this.clearContainer();
|
|
223
|
+
const data = this.prepareChartData(natalChart, transitDate);
|
|
224
|
+
// Create chart with theme colors
|
|
225
|
+
const settings = {
|
|
226
|
+
SYMBOL_SCALE: 1.2,
|
|
227
|
+
STROKE_ONLY: false,
|
|
228
|
+
...getThemeSettings(theme, false),
|
|
229
|
+
};
|
|
230
|
+
const ChartClass = (Chart.default ||
|
|
231
|
+
Chart);
|
|
232
|
+
const chart = new ChartClass('chart-container', 800, 800, settings);
|
|
233
|
+
// Generate SVG
|
|
234
|
+
const radix = chart.radix(data);
|
|
235
|
+
radix.aspects();
|
|
236
|
+
radix.transit(data);
|
|
237
|
+
const svgString = this.extractSVG();
|
|
238
|
+
// Convert to requested format
|
|
239
|
+
if (format === 'svg') {
|
|
240
|
+
return svgString;
|
|
241
|
+
}
|
|
242
|
+
return this.convertToImage(svgString, format, theme);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Map internal planet names to astrochart keys
|
|
246
|
+
*
|
|
247
|
+
* @param planetName - Internal planet name
|
|
248
|
+
* @returns Astrochart planet key or null if not supported
|
|
249
|
+
*
|
|
250
|
+
* @remarks
|
|
251
|
+
* Maps our planet names to the keys expected by astrochart.
|
|
252
|
+
* Uses mean node to avoid collision with true node.
|
|
253
|
+
*/
|
|
254
|
+
getPlanetKey(planetName) {
|
|
255
|
+
const mapping = {
|
|
256
|
+
Sun: 'Sun',
|
|
257
|
+
Moon: 'Moon',
|
|
258
|
+
Mercury: 'Mercury',
|
|
259
|
+
Venus: 'Venus',
|
|
260
|
+
Mars: 'Mars',
|
|
261
|
+
Jupiter: 'Jupiter',
|
|
262
|
+
Saturn: 'Saturn',
|
|
263
|
+
Uranus: 'Uranus',
|
|
264
|
+
Neptune: 'Neptune',
|
|
265
|
+
Pluto: 'Pluto',
|
|
266
|
+
Chiron: 'Chiron',
|
|
267
|
+
'North Node (Mean)': 'NNode', // Use mean node for consistency
|
|
268
|
+
// True node intentionally omitted - prevents collision/overwrite
|
|
269
|
+
};
|
|
270
|
+
return mapping[planetName] || null;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Extract SVG string from virtual DOM
|
|
274
|
+
*
|
|
275
|
+
* @returns SVG string
|
|
276
|
+
* @throws Error if no SVG element found
|
|
277
|
+
*
|
|
278
|
+
* @remarks
|
|
279
|
+
* Finds the SVG element in the virtual DOM and returns
|
|
280
|
+
* its outer HTML. Throws if no chart was rendered.
|
|
281
|
+
*/
|
|
282
|
+
extractSVG() {
|
|
283
|
+
// Get the SVG element from the virtual DOM
|
|
284
|
+
const container = this.dom.window.document.getElementById('chart-container');
|
|
285
|
+
if (!container) {
|
|
286
|
+
throw new Error('Chart container not found - DOM setup failed');
|
|
287
|
+
}
|
|
288
|
+
const svg = container.querySelector('svg');
|
|
289
|
+
if (!svg) {
|
|
290
|
+
throw new Error('Chart rendering failed - no SVG element generated by astrochart library');
|
|
291
|
+
}
|
|
292
|
+
return svg.outerHTML;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Convert SVG to specified output format
|
|
296
|
+
*
|
|
297
|
+
* @param svg - SVG string or buffer
|
|
298
|
+
* @param format - Target output format
|
|
299
|
+
* @returns Converted image as buffer (png/webp) or data URL (svg)
|
|
300
|
+
* @throws Error if conversion fails
|
|
301
|
+
*
|
|
302
|
+
* @remarks
|
|
303
|
+
* Uses Sharp library for PNG/WebP conversion. SVG is returned
|
|
304
|
+
* as a data URL for direct use in web contexts.
|
|
305
|
+
*/
|
|
306
|
+
async convertToImage(svg, format, theme) {
|
|
307
|
+
const buffer = Buffer.from(svg);
|
|
308
|
+
// Use theme-appropriate background color
|
|
309
|
+
const bgColor = theme === 'dark'
|
|
310
|
+
? { r: 40, g: 44, b: 52, alpha: 1 } // #282c34
|
|
311
|
+
: { r: 255, g: 255, b: 255, alpha: 1 }; // #ffffff
|
|
312
|
+
if (format === 'png') {
|
|
313
|
+
return sharp(buffer)
|
|
314
|
+
.flatten({ background: bgColor })
|
|
315
|
+
.png({ quality: 100, compressionLevel: 6 })
|
|
316
|
+
.toBuffer();
|
|
317
|
+
}
|
|
318
|
+
// WebP
|
|
319
|
+
return sharp(buffer)
|
|
320
|
+
.flatten({ background: bgColor })
|
|
321
|
+
.webp({ quality: 95, effort: 6 })
|
|
322
|
+
.toBuffer();
|
|
323
|
+
}
|
|
324
|
+
}
|