swisseph-wasm
Version:
High-precision Swiss Ephemeris WebAssembly library for astronomical calculations in JavaScript
388 lines (334 loc) • 12.6 kB
JavaScript
/**
* Birth Chart Calculation Example
*
* This example demonstrates how to calculate a complete birth chart
* including planetary positions, houses, and various coordinate systems.
*/
import SwissEph from '../src/swisseph.js';
/**
* Birth Chart Calculator Class
* Provides methods for calculating comprehensive birth charts
*/
class BirthChartCalculator {
constructor() {
this.swe = null;
this.initialized = false;
}
/**
* Initialize the Swiss Ephemeris
*/
async init() {
if (!this.initialized) {
this.swe = new SwissEph();
await this.swe.initSwissEph();
this.initialized = true;
}
}
/**
* Calculate a complete birth chart
* @param {Object} birthData - Birth information
* @param {number} birthData.year - Birth year
* @param {number} birthData.month - Birth month (1-12)
* @param {number} birthData.day - Birth day
* @param {number} birthData.hour - Birth hour (0-23)
* @param {number} birthData.minute - Birth minute (0-59)
* @param {number} birthData.timezone - Timezone offset from UTC
* @param {number} birthData.latitude - Birth latitude
* @param {number} birthData.longitude - Birth longitude
* @param {string} birthData.houseSystem - House system ('P', 'K', 'E', etc.)
* @returns {Object} Complete birth chart data
*/
async calculateBirthChart(birthData) {
await this.init();
const {
year, month, day, hour, minute, timezone,
latitude, longitude, houseSystem = 'P'
} = birthData;
// Convert local time to UTC
const utcHour = hour + minute / 60 - timezone;
const jd = this.swe.julday(year, month, day, utcHour);
// Calculate planetary positions
const planets = await this.calculatePlanets(jd);
// Calculate houses
const houses = await this.calculateHouses(jd, latitude, longitude, houseSystem);
// Calculate additional points
const additionalPoints = await this.calculateAdditionalPoints(jd);
// Calculate aspects
const aspects = this.calculateAspects(planets);
return {
birthData: {
...birthData,
julianDay: jd,
utcTime: utcHour
},
planets,
houses,
additionalPoints,
aspects,
metadata: {
calculatedAt: new Date().toISOString(),
swissEphVersion: this.swe.version()
}
};
}
/**
* Calculate positions for all major planets
* @param {number} jd - Julian Day
* @returns {Object} Planet positions
*/
async calculatePlanets(jd) {
const planetList = [
{ id: this.swe.SE_SUN, name: 'Sun', symbol: '☉' },
{ id: this.swe.SE_MOON, name: 'Moon', symbol: '☽' },
{ id: this.swe.SE_MERCURY, name: 'Mercury', symbol: '☿' },
{ id: this.swe.SE_VENUS, name: 'Venus', symbol: '♀' },
{ id: this.swe.SE_MARS, name: 'Mars', symbol: '♂' },
{ id: this.swe.SE_JUPITER, name: 'Jupiter', symbol: '♃' },
{ id: this.swe.SE_SATURN, name: 'Saturn', symbol: '♄' },
{ id: this.swe.SE_URANUS, name: 'Uranus', symbol: '♅' },
{ id: this.swe.SE_NEPTUNE, name: 'Neptune', symbol: '♆' },
{ id: this.swe.SE_PLUTO, name: 'Pluto', symbol: '♇' }
];
const planets = {};
for (const planet of planetList) {
const position = this.swe.calc_ut(jd, planet.id, this.swe.SEFLG_SWIEPH | this.swe.SEFLG_SPEED);
planets[planet.name] = {
id: planet.id,
name: planet.name,
symbol: planet.symbol,
longitude: position[0],
latitude: position[1],
distance: position[2],
speed: position[3],
zodiacSign: this.getZodiacSign(position[0]),
house: null // Will be calculated later with houses
};
}
return planets;
}
/**
* Calculate house cusps and angles
* @param {number} jd - Julian Day
* @param {number} latitude - Geographic latitude
* @param {number} longitude - Geographic longitude
* @param {string} houseSystem - House system
* @returns {Object} House information
*/
async calculateHouses(jd, latitude, longitude, houseSystem) {
// Calculate houses using the specified system
const houseResult = this.swe.houses(jd, latitude, longitude, houseSystem);
// Get sidereal time for ARMC calculation
const sidTime = this.swe.sidtime(jd);
const armc = (sidTime + longitude / 15) * 15; // Convert to degrees
const houses = {
system: houseSystem,
cusps: [],
angles: {
ascendant: null,
midheaven: null,
descendant: null,
imumCoeli: null
}
};
// Extract house cusps (houses 1-12)
for (let i = 1; i <= 12; i++) {
houses.cusps[i] = {
house: i,
longitude: houseResult, // This would need proper implementation
zodiacSign: this.getZodiacSign(houseResult)
};
}
// Calculate angles
houses.angles.ascendant = houses.cusps[1].longitude;
houses.angles.midheaven = houses.cusps[10].longitude;
houses.angles.descendant = (houses.cusps[1].longitude + 180) % 360;
houses.angles.imumCoeli = (houses.cusps[10].longitude + 180) % 360;
return houses;
}
/**
* Calculate additional astrological points
* @param {number} jd - Julian Day
* @returns {Object} Additional points
*/
async calculateAdditionalPoints(jd) {
const points = {};
// Lunar nodes
const meanNode = this.swe.calc_ut(jd, this.swe.SE_MEAN_NODE, this.swe.SEFLG_SWIEPH);
const trueNode = this.swe.calc_ut(jd, this.swe.SE_TRUE_NODE, this.swe.SEFLG_SWIEPH);
points.meanNode = {
name: 'Mean North Node',
symbol: '☊',
longitude: meanNode[0],
zodiacSign: this.getZodiacSign(meanNode[0])
};
points.trueNode = {
name: 'True North Node',
symbol: '☊',
longitude: trueNode[0],
zodiacSign: this.getZodiacSign(trueNode[0])
};
// South nodes (opposite of north nodes)
points.meanSouthNode = {
name: 'Mean South Node',
symbol: '☋',
longitude: (meanNode[0] + 180) % 360,
zodiacSign: this.getZodiacSign((meanNode[0] + 180) % 360)
};
// Lunar apogee (Lilith)
const meanApogee = this.swe.calc_ut(jd, this.swe.SE_MEAN_APOG, this.swe.SEFLG_SWIEPH);
points.lilith = {
name: 'Mean Lilith',
symbol: '⚸',
longitude: meanApogee[0],
zodiacSign: this.getZodiacSign(meanApogee[0])
};
// Chiron
const chiron = this.swe.calc_ut(jd, this.swe.SE_CHIRON, this.swe.SEFLG_SWIEPH);
points.chiron = {
name: 'Chiron',
symbol: '⚷',
longitude: chiron[0],
zodiacSign: this.getZodiacSign(chiron[0])
};
return points;
}
/**
* Calculate major aspects between planets
* @param {Object} planets - Planet positions
* @returns {Array} List of aspects
*/
calculateAspects(planets) {
const aspects = [];
const planetNames = Object.keys(planets);
// Major aspects with their angles and orbs
const aspectTypes = [
{ name: 'Conjunction', angle: 0, orb: 8, symbol: '☌' },
{ name: 'Opposition', angle: 180, orb: 8, symbol: '☍' },
{ name: 'Trine', angle: 120, orb: 6, symbol: '△' },
{ name: 'Square', angle: 90, orb: 6, symbol: '□' },
{ name: 'Sextile', angle: 60, orb: 4, symbol: '⚹' },
{ name: 'Quincunx', angle: 150, orb: 3, symbol: '⚻' }
];
// Check all planet pairs
for (let i = 0; i < planetNames.length; i++) {
for (let j = i + 1; j < planetNames.length; j++) {
const planet1 = planets[planetNames[i]];
const planet2 = planets[planetNames[j]];
const angle = Math.abs(planet1.longitude - planet2.longitude);
const normalizedAngle = angle > 180 ? 360 - angle : angle;
// Check each aspect type
for (const aspectType of aspectTypes) {
const difference = Math.abs(normalizedAngle - aspectType.angle);
if (difference <= aspectType.orb) {
aspects.push({
planet1: planet1.name,
planet2: planet2.name,
aspect: aspectType.name,
symbol: aspectType.symbol,
angle: aspectType.angle,
actualAngle: normalizedAngle,
orb: difference,
applying: planet1.speed > planet2.speed
});
}
}
}
}
return aspects.sort((a, b) => a.orb - b.orb);
}
/**
* Get zodiac sign information for a longitude
* @param {number} longitude - Longitude in degrees
* @returns {Object} Zodiac sign information
*/
getZodiacSign(longitude) {
const signs = [
{ name: 'Aries', symbol: '♈', element: 'Fire', quality: 'Cardinal' },
{ name: 'Taurus', symbol: '♉', element: 'Earth', quality: 'Fixed' },
{ name: 'Gemini', symbol: '♊', element: 'Air', quality: 'Mutable' },
{ name: 'Cancer', symbol: '♋', element: 'Water', quality: 'Cardinal' },
{ name: 'Leo', symbol: '♌', element: 'Fire', quality: 'Fixed' },
{ name: 'Virgo', symbol: '♍', element: 'Earth', quality: 'Mutable' },
{ name: 'Libra', symbol: '♎', element: 'Air', quality: 'Cardinal' },
{ name: 'Scorpio', symbol: '♏', element: 'Water', quality: 'Fixed' },
{ name: 'Sagittarius', symbol: '♐', element: 'Fire', quality: 'Mutable' },
{ name: 'Capricorn', symbol: '♑', element: 'Earth', quality: 'Cardinal' },
{ name: 'Aquarius', symbol: '♒', element: 'Air', quality: 'Fixed' },
{ name: 'Pisces', symbol: '♓', element: 'Water', quality: 'Mutable' }
];
const signIndex = Math.floor(longitude / 30);
const degree = longitude % 30;
return {
...signs[signIndex],
degree: degree,
formatted: `${degree.toFixed(2)}° ${signs[signIndex].name}`
};
}
/**
* Clean up resources
*/
destroy() {
if (this.swe) {
this.swe.close();
this.swe = null;
this.initialized = false;
}
}
}
/**
* Example usage of the Birth Chart Calculator
*/
async function exampleBirthChart() {
console.log('=== Birth Chart Calculation Example ===\n');
const calculator = new BirthChartCalculator();
try {
// Example birth data
const birthData = {
year: 1990,
month: 5,
day: 15,
hour: 14,
minute: 30,
timezone: -5, // UTC-5 (Eastern Daylight Time)
latitude: 40.7128, // New York City
longitude: -74.0060,
houseSystem: 'P' // Placidus
};
console.log('Calculating birth chart for:');
console.log(`Date: ${birthData.year}-${birthData.month.toString().padStart(2, '0')}-${birthData.day.toString().padStart(2, '0')}`);
console.log(`Time: ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')} (UTC${birthData.timezone})`);
console.log(`Location: ${birthData.latitude}°N, ${Math.abs(birthData.longitude)}°W\n`);
const chart = await calculator.calculateBirthChart(birthData);
// Display planetary positions
console.log('PLANETARY POSITIONS:');
console.log('===================');
for (const [name, planet] of Object.entries(chart.planets)) {
console.log(`${planet.symbol} ${name.padEnd(10)}: ${planet.zodiacSign.formatted} (${planet.longitude.toFixed(2)}°)`);
}
// Display additional points
console.log('\nADDITIONAL POINTS:');
console.log('==================');
for (const [name, point] of Object.entries(chart.additionalPoints)) {
console.log(`${point.symbol} ${point.name.padEnd(15)}: ${point.zodiacSign.formatted} (${point.longitude.toFixed(2)}°)`);
}
// Display major aspects
console.log('\nMAJOR ASPECTS:');
console.log('==============');
chart.aspects.slice(0, 10).forEach(aspect => {
console.log(`${aspect.planet1} ${aspect.symbol} ${aspect.planet2}: ${aspect.aspect} (orb: ${aspect.orb.toFixed(2)}°)`);
});
console.log(`\nTotal aspects found: ${chart.aspects.length}`);
console.log(`Calculated using Swiss Ephemeris ${chart.metadata.swissEphVersion}`);
} catch (error) {
console.error('Error calculating birth chart:', error);
} finally {
calculator.destroy();
}
}
// Export the calculator class and example function
export { BirthChartCalculator, exampleBirthChart };
// Run example if this file is executed directly
// Check if running in Node.js and if this is the main module
if (typeof process !== 'undefined' && process.argv && import.meta.url === `file://${process.argv[1]}`) {
exampleBirthChart().catch(console.error);
}