klio 1.2.6 → 1.2.9

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 CHANGED
@@ -119,6 +119,7 @@ Aspekte für Mond:
119
119
 
120
120
  Diese Analyse basiert auf der aktuellen Planetenposition.
121
121
  ```
122
+ Kombiniere mit `--i --tr` und `--hs koch` für persönliche Transit-Aspekte
122
123
 
123
124
  ### Aspekte für einen Planeten (Geburtshoroskop)
124
125
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klio",
3
- "version": "1.2.6",
3
+ "version": "1.2.9",
4
4
  "description": "Eine CLI für astrologische Berechnungen",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -148,7 +148,6 @@ function calculateHouses(julianDay, houseSystem = 'K', useBirthLocation = false)
148
148
  latitude = config.birthData.location.latitude;
149
149
  longitude = config.birthData.location.longitude;
150
150
  locationName = `${config.birthData.location.name}, ${config.birthData.location.country}`;
151
- console.log(`Verwende Geburtsort: ${locationName} (${latitude}° Breitengrad, ${longitude}° Längengrad)`);
152
151
  } else if (useBirthLocation) {
153
152
  // Geburtsort ist konfiguriert, aber kein Standort - verwende Standardort
154
153
  latitude = defaultLatitude;
@@ -269,6 +268,9 @@ function getAstrologicalData(planetName, customDate = null) {
269
268
 
270
269
  // Verwende das angegebene Datum oder das aktuelle Datum (mit Zeitzonenberücksichtigung)
271
270
  let calcYear, calcMonth, calcDay, calcHour, calcMinute;
271
+ let timezoneOffsetMinutes;
272
+ let timezone = null;
273
+ let baseDate;
272
274
 
273
275
  if (customDate) {
274
276
  calcYear = customDate.year;
@@ -276,6 +278,18 @@ function getAstrologicalData(planetName, customDate = null) {
276
278
  calcDay = customDate.day;
277
279
  calcHour = customDate.hour;
278
280
  calcMinute = customDate.minute;
281
+ baseDate = customDate;
282
+ // Versuche, die Zeitzone vom customDate abzuleiten (z.B. Geburtsort)
283
+ if (customDate.location && customDate.location.timezone) {
284
+ timezone = customDate.location.timezone;
285
+ } else {
286
+ try {
287
+ const config = loadConfig();
288
+ if (config && config.currentLocation && config.currentLocation.timezone) {
289
+ timezone = config.currentLocation.timezone;
290
+ }
291
+ } catch (_) {}
292
+ }
279
293
  } else {
280
294
  // Verwende die aktuelle Zeit in der konfigurierten Zeitzone
281
295
  const timeData = getCurrentTimeInTimezone();
@@ -284,9 +298,24 @@ function getAstrologicalData(planetName, customDate = null) {
284
298
  calcDay = timeData.day;
285
299
  calcHour = timeData.hour;
286
300
  calcMinute = timeData.minute;
301
+ baseDate = timeData;
302
+ try {
303
+ const config = loadConfig();
304
+ if (config && config.currentLocation && config.currentLocation.timezone) {
305
+ timezone = config.currentLocation.timezone;
306
+ }
307
+ } catch (_) {}
287
308
  }
288
309
 
289
- const julianDay = swisseph.swe_julday(calcYear, calcMonth, calcDay, calcHour + calcMinute / 60, swisseph.SE_GREG_CAL);
310
+ // Bestimme Offset in Minuten (Lokalzeit - UTC)
311
+ if (timezone) {
312
+ timezoneOffsetMinutes = getTimezoneOffset({ year: calcYear, month: calcMonth, day: calcDay, hour: calcHour, minute: calcMinute }, timezone);
313
+ } else {
314
+ // Fallback: Systemoffset umkehren, damit (Lokalzeit - UTC) entsteht
315
+ timezoneOffsetMinutes = -new Date().getTimezoneOffset();
316
+ }
317
+
318
+ const julianDay = calculateJulianDayUTC({ year: calcYear, month: calcMonth, day: calcDay, hour: calcHour, minute: calcMinute }, timezoneOffsetMinutes);
290
319
  const flag = swisseph.SEFLG_SWIEPH | swisseph.SEFLG_SPEED;
291
320
  const result = swisseph.swe_calc_ut(julianDay, planet, flag);
292
321
 
@@ -341,13 +370,18 @@ function getCriticalPlanets(customDate = null) {
341
370
  timeData = getCurrentTimeInTimezone();
342
371
  }
343
372
 
344
- const julianDay = swisseph.swe_julday(
345
- timeData.year,
346
- timeData.month,
347
- timeData.day,
348
- timeData.hour + timeData.minute / 60,
349
- swisseph.SE_GREG_CAL
350
- );
373
+ // Berücksichtige die lokale Zeitzone für die Umrechnung nach UTC
374
+ let timezone = null;
375
+ try {
376
+ const config = loadConfig();
377
+ if (customDate && customDate.location && customDate.location.timezone) {
378
+ timezone = customDate.location.timezone;
379
+ } else if (config && config.currentLocation && config.currentLocation.timezone) {
380
+ timezone = config.currentLocation.timezone;
381
+ }
382
+ } catch (_) {}
383
+ const offsetMinutes = timezone ? getTimezoneOffset(timeData, timezone) : -new Date().getTimezoneOffset();
384
+ const julianDay = calculateJulianDayUTC(timeData, offsetMinutes);
351
385
 
352
386
  // Berechne Positionen aller Planeten
353
387
  for (const [name, planetId] of Object.entries(planets)) {
@@ -480,19 +514,19 @@ function findExactAspectTime(planet1, planet2, aspectType, startDate) {
480
514
  return null;
481
515
  }
482
516
 
483
- // Wir suchen in einem größeren Zeitfenster (180 Tage vor und nach dem Startdatum)
484
- // um den exakten Aspekt zu finden, egal ob in Vergangenheit oder Zukunft
517
+ // Wir suchen in einem vernünftigen Zeitfenster (90 Tage in der Zukunft)
518
+ // um den nächsten exakten Aspekt zu finden
485
519
  // Besonders wichtig für langsame Planeten wie Saturn, Uranus, Neptun, Pluto
486
520
  let bestMatch = null;
487
521
  let bestDistance = Infinity;
488
522
 
489
- // Suche in einem Zeitfenster von 180 Tagen vor und nach dem Startdatum
490
- const searchWindow = 180; // Tage
523
+ // Suche nur in der Zukunft (0-90 Tage nach dem Startdatum)
524
+ const searchWindow = 90; // Tage
491
525
 
492
- // Wir suchen auch in kleineren Schritten (alle 3 Stunden), um präzisere Ergebnisse zu erhalten
493
- const stepsPerDay = 8; // 3-Stunden-Schritte
526
+ // Wir suchen in kleineren Schritten (alle 2 Stunden), um präzisere Ergebnisse zu erhalten
527
+ const stepsPerDay = 12; // 2-Stunden-Schritte
494
528
 
495
- for (let daysOffset = -searchWindow; daysOffset <= searchWindow; daysOffset++) {
529
+ for (let daysOffset = 0; daysOffset <= searchWindow; daysOffset++) {
496
530
  for (let hourOffset = 0; hourOffset < 24; hourOffset++) { // Jede Stunde
497
531
  for (let minuteOffset = 0; minuteOffset < 60; minuteOffset += 5) { // Alle 5 Minuten
498
532
  const testDate = addDays(startDate, daysOffset);
@@ -518,7 +552,56 @@ function findExactAspectTime(planet1, planet2, aspectType, startDate) {
518
552
  }
519
553
 
520
554
  // Wenn wir einen Aspekt gefunden haben, der nah genug am Ziel ist, geben wir ihn zurück
521
- if (bestMatch && bestDistance <= 1.0) { // Toleranz von 1.0°
555
+ // Strengere Toleranz von 0. für bessere Genauigkeit
556
+ if (bestMatch && bestDistance <= 0.5) { // Toleranz von 0.5°
557
+ return bestMatch;
558
+ }
559
+
560
+ return null;
561
+ }
562
+
563
+ // Funktion zum Finden des letzten exakten Aspekts in der Vergangenheit (für separative Phasen)
564
+ function findLastExactAspectTime(planet1, planet2, aspectType, startDate) {
565
+ const targetAngle = getAspectAngle(aspectType);
566
+
567
+ if (targetAngle === null) {
568
+ return null;
569
+ }
570
+
571
+ // Suche in der Vergangenheit (bis zu 180 Tage vor dem Startdatum)
572
+ let bestMatch = null;
573
+ let bestDistance = Infinity;
574
+
575
+ // Suche in einem Zeitfenster von 180 Tagen vor dem Startdatum
576
+ const searchWindow = 180; // Tage
577
+
578
+ for (let daysOffset = -searchWindow; daysOffset <= 0; daysOffset++) {
579
+ for (let hourOffset = 0; hourOffset < 24; hourOffset++) { // Jede Stunde
580
+ for (let minuteOffset = 0; minuteOffset < 60; minuteOffset += 5) { // Alle 5 Minuten
581
+ const testDate = addDays(startDate, daysOffset);
582
+ testDate.hour = hourOffset;
583
+ testDate.minute = minuteOffset;
584
+
585
+ // Berechne die Positionen der beiden Planeten
586
+ const planet1Data = getAstrologicalData(planet1, testDate);
587
+ const planet2Data = getAstrologicalData(planet2, testDate);
588
+
589
+ // Berechne den Winkel zwischen den Planeten
590
+ const angleDiff = Math.abs(planet1Data.longitude - planet2Data.longitude) % 360;
591
+ const normalizedAngle = Math.min(angleDiff, 360 - angleDiff);
592
+ const distanceToTarget = Math.abs(normalizedAngle - targetAngle);
593
+
594
+ // Speichere das beste Ergebnis
595
+ if (distanceToTarget < bestDistance) {
596
+ bestDistance = distanceToTarget;
597
+ bestMatch = {...testDate};
598
+ }
599
+ }
600
+ }
601
+ }
602
+
603
+ // Wenn wir einen Aspekt gefunden haben, der nah genug am Ziel ist, geben wir ihn zurück
604
+ if (bestMatch && bestDistance <= 0.5) { // Toleranz von 0.5°
522
605
  return bestMatch;
523
606
  }
524
607
 
@@ -565,13 +648,16 @@ function isPlanetRetrograde(planetName, dateComponents) {
565
648
  if (planet === undefined) return false;
566
649
 
567
650
  // Berechne die Position mit Geschwindigkeitsinformation
568
- const julianDay = swisseph.swe_julday(
569
- dateComponents.year,
570
- dateComponents.month,
571
- dateComponents.day,
572
- dateComponents.hour + dateComponents.minute / 60,
573
- swisseph.SE_GREG_CAL
574
- );
651
+ // Konvertiere Datum zu UTC basierend auf konfigurierter Zeitzone
652
+ let tz = null;
653
+ try {
654
+ const config = loadConfig();
655
+ if (config && config.currentLocation && config.currentLocation.timezone) {
656
+ tz = config.currentLocation.timezone;
657
+ }
658
+ } catch (_) {}
659
+ const off = tz ? getTimezoneOffset(dateComponents, tz) : -new Date().getTimezoneOffset();
660
+ const julianDay = calculateJulianDayUTC(dateComponents, off);
575
661
 
576
662
  const flag = swisseph.SEFLG_SWIEPH | swisseph.SEFLG_SPEED;
577
663
  const result = swisseph.swe_calc_ut(julianDay, planet, flag);
@@ -1132,6 +1218,213 @@ async function calculatePersonalTransits(transitDate = null, birthData = null) {
1132
1218
  };
1133
1219
  }
1134
1220
 
1221
+ // Funktion zur Berechnung persönlicher Transit-Aspekte
1222
+ function calculatePersonalTransitAspects(transitDate = null, birthData = null, targetPlanet = null) {
1223
+ if (!birthData) {
1224
+ birthData = getBirthDataFromConfig();
1225
+ if (!birthData) {
1226
+ console.error('Keine Geburtsdaten verfügbar für persönliche Transit-Aspekte.');
1227
+ return null;
1228
+ }
1229
+ }
1230
+
1231
+ // Verwende aktuelles Datum, wenn kein Transitdatum angegeben
1232
+ if (!transitDate) {
1233
+ transitDate = getCurrentTimeInTimezone();
1234
+ }
1235
+
1236
+ // Berechne aktuelle Planetenpositionen (Transit)
1237
+ const transitPlanets = {};
1238
+ for (const [name, planetId] of Object.entries(planets)) {
1239
+ const data = getAstrologicalData(name, transitDate);
1240
+ transitPlanets[name] = data.longitude;
1241
+ }
1242
+
1243
+ // Berechne Geburtsplanetenpositionen (Radix)
1244
+ const birthPlanets = {};
1245
+ for (const [name, planetId] of Object.entries(planets)) {
1246
+ const data = getAstrologicalData(name, birthData);
1247
+ birthPlanets[name] = data.longitude;
1248
+ }
1249
+
1250
+ // Berechne Aspekte zwischen Transit- und Geburtsplaneten
1251
+ const aspects = [];
1252
+ const aspectTypes = [
1253
+ { name: 'Konjunktion', angle: 0, orb: 8 },
1254
+ { name: 'Opposition', angle: 180, orb: 8 },
1255
+ { name: 'Quadrat', angle: 90, orb: 6 },
1256
+ { name: 'Trigon', angle: 120, orb: 6 },
1257
+ { name: 'Sextil', angle: 60, orb: 4 }
1258
+ ];
1259
+
1260
+ // Vergleiche jeden Transitplaneten mit jedem Geburtsplaneten
1261
+ for (const [transitName, transitLongitude] of Object.entries(transitPlanets)) {
1262
+ for (const [birthName, birthLongitude] of Object.entries(birthPlanets)) {
1263
+ // Überspringe, wenn es sich um denselben Planeten handelt
1264
+ if (transitName === birthName) continue;
1265
+
1266
+ // Berechne den Winkelunterschied
1267
+ const angleDiff = Math.abs(transitLongitude - birthLongitude) % 360;
1268
+ const normalizedAngle = Math.min(angleDiff, 360 - angleDiff);
1269
+
1270
+ // Prüfe auf Aspekte
1271
+ for (const aspect of aspectTypes) {
1272
+ if (Math.abs(normalizedAngle - aspect.angle) <= aspect.orb) {
1273
+ aspects.push({
1274
+ transitPlanet: transitName,
1275
+ birthPlanet: birthName,
1276
+ type: aspect.name,
1277
+ angle: normalizedAngle.toFixed(2),
1278
+ orb: Math.abs(normalizedAngle - aspect.angle).toFixed(2),
1279
+ transitLongitude: transitLongitude,
1280
+ birthLongitude: birthLongitude
1281
+ });
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+
1287
+ // Filtere nach Zielplanet, falls angegeben
1288
+ const filteredAspects = targetPlanet
1289
+ ? aspects.filter(aspect => aspect.transitPlanet === targetPlanet)
1290
+ : aspects;
1291
+
1292
+ return {
1293
+ transitDate: transitDate,
1294
+ birthData: birthData,
1295
+ aspects: filteredAspects
1296
+ };
1297
+ }
1298
+
1299
+ // Funktion zur Anzeige persönlicher Transit-Aspekte
1300
+ function showPersonalTransitAspects(transitDate = null, birthData = null, targetPlanet = null) {
1301
+ const aspectData = calculatePersonalTransitAspects(transitDate, birthData, targetPlanet);
1302
+
1303
+ if (!aspectData || aspectData.aspects.length === 0) {
1304
+ console.log('Keine persönlichen Transit-Aspekte gefunden.');
1305
+ return;
1306
+ }
1307
+
1308
+ const transitDateDisplay = transitDate || getCurrentTimeInTimezone();
1309
+ console.log(`Persönliche Transit-Aspekte (${transitDateDisplay.day}.${transitDateDisplay.month}.${transitDateDisplay.year}):`);
1310
+ console.log('================================================================================');
1311
+ console.log('| Transit | Geburts | Aspekt | Orb | Transit Pos | Geburts Pos |');
1312
+ console.log('================================================================================');
1313
+
1314
+ // Hole zusätzliche Informationen für die Anzeige
1315
+ const transitPlanetsData = {};
1316
+ const birthPlanetsData = {};
1317
+
1318
+ for (const [name] of Object.entries(planets)) {
1319
+ transitPlanetsData[name] = getAstrologicalData(name, transitDateDisplay);
1320
+ birthPlanetsData[name] = getAstrologicalData(name, birthData);
1321
+ }
1322
+
1323
+ aspectData.aspects.forEach(aspect => {
1324
+ const transitPlanetData = transitPlanetsData[aspect.transitPlanet];
1325
+ const birthPlanetData = birthPlanetsData[aspect.birthPlanet];
1326
+
1327
+ const transitPlanetFormatted = aspect.transitPlanet.charAt(0).toUpperCase() + aspect.transitPlanet.slice(1);
1328
+ const birthPlanetFormatted = aspect.birthPlanet.charAt(0).toUpperCase() + aspect.birthPlanet.slice(1);
1329
+ const aspectName = aspect.type.padEnd(11, ' ');
1330
+ const orb = aspect.orb.padEnd(4, ' ');
1331
+ const transitPos = `${transitPlanetData.sign} ${transitPlanetData.degreeInSign}°`;
1332
+ const birthPos = `${birthPlanetData.sign} ${birthPlanetData.degreeInSign}°`;
1333
+
1334
+ console.log(`| ${transitPlanetFormatted.padEnd(10)} | ${birthPlanetFormatted.padEnd(10)} | ${aspectName} | ${orb}° | ${transitPos.padEnd(11)} | ${birthPos.padEnd(11)} |`);
1335
+ });
1336
+
1337
+ console.log('================================================================================');
1338
+ console.log(`Gesamt: ${aspectData.aspects.length} persönliche Transit-Aspekte gefunden`);
1339
+ console.log('\nErklärung: Diese Aspekte zeigen, wie die aktuellen Planetenpositionen');
1340
+ console.log('mit deinen Geburtspositionen interagieren (Transit → Radix).');
1341
+ }
1342
+
1343
+ // Funktion für kombinierte Analyse (Geburts- und Transit-Aspekte mit Häusern)
1344
+ async function showCombinedAnalysis(planetName, transitDate = null, birthData = null, houseSystem = 'K') {
1345
+ if (!birthData) {
1346
+ birthData = getBirthDataFromConfig();
1347
+ if (!birthData) {
1348
+ console.error('Keine Geburtsdaten verfügbar für kombinierte Analyse.');
1349
+ return null;
1350
+ }
1351
+ }
1352
+
1353
+ // Verwende aktuelles Datum, wenn kein Transitdatum angegeben
1354
+ if (!transitDate) {
1355
+ transitDate = getCurrentTimeInTimezone();
1356
+ }
1357
+
1358
+ // Berechne Häuser basierend auf Geburtsdaten
1359
+ const birthJulianDay = calculateJulianDayUTC(birthData, getTimezoneOffset(birthData, birthData.location?.timezone || 'Europe/Zurich'));
1360
+ const houses = await calculateHouses(birthJulianDay, houseSystem === 'koch' ? 'K' : houseSystem === 'wholesign' ? 'W' : 'G', true);
1361
+
1362
+ // Berechne aktuelle Planetenpositionen (Transit)
1363
+ const transitPlanetData = getAstrologicalData(planetName, transitDate);
1364
+ const transitLongitude = transitPlanetData.longitude;
1365
+ const transitHouse = getPlanetHouse(transitLongitude, houses.house);
1366
+
1367
+ // Berechne Geburtsplanetenpositionen (Radix)
1368
+ const birthPlanetData = getAstrologicalData(planetName, birthData);
1369
+ const birthLongitude = birthPlanetData.longitude;
1370
+ const birthHouse = getPlanetHouse(birthLongitude, houses.house);
1371
+
1372
+ // Berechne Geburtsaspekte für den Planeten
1373
+ const birthAspects = calculatePlanetAspects(planetName, birthData, true);
1374
+
1375
+ // Berechne Transit-Aspekte für den Planeten
1376
+ const transitAspectsData = calculatePersonalTransitAspects(transitDate, birthData, planetName);
1377
+ const transitAspects = transitAspectsData ? transitAspectsData.aspects : [];
1378
+
1379
+ // Zeige kombinierte Analyse an
1380
+ console.log(`Kombinierte Analyse für ${planetName.charAt(0).toUpperCase() + planetName.slice(1)}:`);
1381
+ console.log('================================================================================');
1382
+ console.log(`Analyse-Datum: ${transitDate.day}.${transitDate.month}.${transitDate.year}`);
1383
+ console.log(`Geburtsdatum: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
1384
+ console.log('');
1385
+
1386
+ // Zeige Planetenpositionen und Häuser
1387
+ console.log('Positionen und Häuser:');
1388
+ console.log('--------------------------------------------------------------------------------');
1389
+ console.log(`Transit: ${transitPlanetData.sign} ${transitPlanetData.degreeInSign}° (Haus ${transitHouse})`);
1390
+ console.log(`Geburt: ${birthPlanetData.sign} ${birthPlanetData.degreeInSign}° (Haus ${birthHouse})`);
1391
+ console.log('');
1392
+
1393
+ // Zeige Geburtsaspekte
1394
+ if (birthAspects.length > 0) {
1395
+ console.log('Geburtsaspekte (Radix → Radix):');
1396
+ console.log('--------------------------------------------------------------------------------');
1397
+ birthAspects.forEach(aspect => {
1398
+ const aspectPlanetData = getAstrologicalData(aspect.planet, birthData);
1399
+ const aspectHouse = getPlanetHouse(parseFloat(aspectPlanetData.degreeInSign) + (signs.indexOf(aspectPlanetData.sign) * 30), houses.house);
1400
+ console.log(`${aspect.type.padEnd(12)} mit ${aspect.planet.padEnd(8)} (${aspectPlanetData.sign} ${aspectPlanetData.degreeInSign}° Haus ${aspectHouse}) - Orb: ${aspect.orb}°`);
1401
+ });
1402
+ console.log('');
1403
+ }
1404
+
1405
+ // Zeige Transit-Aspekte
1406
+ if (transitAspects.length > 0) {
1407
+ console.log('Transit-Aspekte (Transit → Radix):');
1408
+ console.log('--------------------------------------------------------------------------------');
1409
+ transitAspects.forEach(aspect => {
1410
+ const birthPlanetData = getAstrologicalData(aspect.birthPlanet, birthData);
1411
+ const birthHouse = getPlanetHouse(parseFloat(birthPlanetData.degreeInSign) + (signs.indexOf(birthPlanetData.sign) * 30), houses.house);
1412
+ console.log(`${aspect.type.padEnd(12)} mit ${aspect.birthPlanet.padEnd(8)} (${birthPlanetData.sign} ${birthPlanetData.degreeInSign}° Haus ${birthHouse}) - Orb: ${aspect.orb}°`);
1413
+ });
1414
+ console.log('');
1415
+ }
1416
+
1417
+ return {
1418
+ transitPlanetData,
1419
+ birthPlanetData,
1420
+ transitHouse,
1421
+ birthHouse,
1422
+ birthAspects,
1423
+ transitAspects,
1424
+ houses
1425
+ };
1426
+ }
1427
+
1135
1428
  // Funktion zur Analyse der Elementverteilung
1136
1429
  function analyzeElementDistribution(dateComponents, useBirthData = false) {
1137
1430
  const elementCounts = {
@@ -1321,9 +1614,13 @@ module.exports = {
1321
1614
  getAspectAngle,
1322
1615
  getFutureAspects,
1323
1616
  findExactAspectTime,
1617
+ findLastExactAspectTime,
1324
1618
  detectAspectFigures,
1325
1619
  showAspectFigures,
1326
- calculatePersonalTransits
1620
+ calculatePersonalTransits,
1621
+ calculatePersonalTransitAspects,
1622
+ showPersonalTransitAspects,
1623
+ showCombinedAnalysis
1327
1624
  };
1328
1625
 
1329
1626
  // Funktion zur Berechnung vergangener Aspekte zwischen zwei Planeten
package/src/cli/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const { Command } = require('commander');
2
2
  const { planets, signs } = require('../astrology/astrologyConstants');
3
3
  const { showRetrogradePlanets } = require('../astrology/retrogradeService');
4
- const { getCurrentTimeInTimezone, showAspectFigures, analyzeElementDistribution, getTimezoneOffset, calculateJulianDayUTC, calculateHouses, getAstrologicalData, getPlanetHouse, showPlanetAspects, calculatePlanetAspects, getAllActiveAspects, showAllActiveAspects, getBirthDataFromConfig, detectAspectFigures, calculatePersonalTransits } = require('../astrology/astrologyService');
4
+ const { getCurrentTimeInTimezone, showAspectFigures, analyzeElementDistribution, getTimezoneOffset, calculateJulianDayUTC, calculateHouses, getAstrologicalData, getPlanetHouse, showPlanetAspects, calculatePlanetAspects, getAllActiveAspects, showAllActiveAspects, getBirthDataFromConfig, detectAspectFigures, calculatePersonalTransits, showPersonalTransitAspects, showCombinedAnalysis, calculatePersonalTransitAspects, determineAspectPhase, findExactAspectTime, findLastExactAspectTime, getAspectAngle, getFutureAspects, getPastAspects } = require('../astrology/astrologyService');
5
5
  const { performSetup, showConfigStatus, loadConfig, setAIModel, askAIModel } = require('../config/configService');
6
6
  const { parseAppleHealthXML } = require('../health/healthService');
7
7
  const { analyzeStepsByPlanetSign, analyzeStressByPlanetAspects, analyzePlanetAspectsForSleep } = require('../health/healthAnalysis');
@@ -110,6 +110,8 @@ program
110
110
  .option('--el', 'Zeigt die Elementverteilung der Planeten in einem horizontalen Chart an')
111
111
  .option('--af', 'Zeigt aktive Aspektfiguren wie T-Quadrate, Große Trigone, etc. an')
112
112
  .option('--transite', 'Zeigt persönliche Transite basierend auf Geburtsdaten an')
113
+ .option('--transit-aspekte', 'Zeigt persönliche Transit-Aspekte (Transit → Radix) an')
114
+ .option('--tr', 'Zeigt persönliche Transit-Aspekte (Transit → Radix) an (Kurzform)')
113
115
  .option('--v <count>', 'Zeigt vergangene Aspekte zwischen zwei Planeten an (Format: --v <count> planet1 aspectType planet2)')
114
116
  .option('--z <count>', 'Zeigt zukünftige Aspekte zwischen zwei Planeten an (Format: --z <count> planet1 aspectType planet2)')
115
117
  .description('Zeigt astrologische Daten für einen Planeten an')
@@ -408,6 +410,185 @@ program
408
410
  return;
409
411
  }
410
412
 
413
+ // Kombinierte Analyse anzeigen, falls --a --i und --tr (oder --transit-aspekte) zusammen mit --hs angegeben
414
+ if ((options.a && shouldUseBirthData(options) && (options.transitAspekte || options.tr)) ||
415
+ (options.transitAspekte || options.tr) && options.hs) {
416
+
417
+ // Geburtsdaten sind erforderlich
418
+ const birthData = getBirthDataFromConfig();
419
+ if (!birthData) {
420
+ console.error('Fehler: Kombinierte Analyse erfordert Geburtsdaten. Bitte führe --setup aus.');
421
+ process.exit(1);
422
+ }
423
+
424
+ // Benutzerdefiniertes Datum verwenden, falls angegeben
425
+ let transitDate = null;
426
+ if (options.d) {
427
+ // Versuche, das Datum als DD.MM.YYYY oder DD.MM.YYYY HH:MM zu parsen
428
+ const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
429
+ const match = options.d.match(dateRegex);
430
+
431
+ if (match) {
432
+ const day = parseInt(match[1], 10);
433
+ const month = parseInt(match[2], 10);
434
+ const year = parseInt(match[3], 10);
435
+ const hour = match[4] ? parseInt(match[4], 10) : 12; // Standard: 12 Uhr
436
+ const minute = match[5] ? parseInt(match[5], 10) : 0; // Standard: 0 Minuten
437
+
438
+ // Überprüfe, ob das Datum gültig ist
439
+ const date = new Date(year, month - 1, day, hour, minute);
440
+ if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
441
+ transitDate = {
442
+ day: day,
443
+ month: month,
444
+ year: year,
445
+ hour: hour,
446
+ minute: minute
447
+ };
448
+ } else {
449
+ console.error('Ungültiges Datum:', options.d);
450
+ console.error('💡 Bitte verwende das Format: DD.MM.YYYY oder "DD.MM.YYYY HH:MM"');
451
+ process.exit(1);
452
+ }
453
+ } else {
454
+ console.error('Ungültiges Datum:', options.d);
455
+ console.error('💡 Bitte verwende das Format: DD.MM.YYYY oder "DD.MM.YYYY HH:MM"');
456
+ process.exit(1);
457
+ }
458
+ }
459
+
460
+ // Haus-System bestimmen
461
+ const houseSystem = options.hs ? options.hs.toLowerCase() : 'koch';
462
+
463
+ // Zeige kombinierte Analyse an
464
+ const targetPlanet = planetArg ? planetArg.toLowerCase() : null;
465
+ if (targetPlanet) {
466
+ await showCombinedAnalysis(targetPlanet, transitDate, birthData, houseSystem);
467
+ } else {
468
+ // Wenn kein Planet angegeben ist, zeige NUR die Transit-Aspekte-Tabelle
469
+ console.log('Transit-Aspekte (Transit → Radix):');
470
+ console.log('================================================================================');
471
+ console.log('| Transit | Aspekt | Mit Planet | Orb | Transit Pos | Transit Haus | Geburts Haus | Phase | Exaktes Datum/Uhrzeit |');
472
+ console.log('================================================================================');
473
+
474
+ // Berechne Häuser einmal für alle Planeten
475
+ const birthJulianDay = calculateJulianDayUTC(birthData, getTimezoneOffset(birthData, birthData.location?.timezone || 'Europe/Zurich'));
476
+ const houses = await calculateHouses(birthJulianDay, houseSystem === 'koch' ? 'K' : houseSystem === 'wholesign' ? 'W' : 'G', true);
477
+ const transitDateDisplay = transitDate || getCurrentTimeInTimezone();
478
+
479
+ // Zeige Transit-Aspekte für alle Planeten
480
+ for (const [planetName] of Object.entries(planets)) {
481
+ const transitPlanetData = getAstrologicalData(planetName, transitDateDisplay);
482
+ const transitHouse = getPlanetHouse(parseFloat(transitPlanetData.degreeInSign) + (signs.indexOf(transitPlanetData.sign) * 30), houses.house);
483
+ const transitAspectsData = calculatePersonalTransitAspects(transitDateDisplay, birthData, planetName);
484
+ const transitAspects = transitAspectsData ? transitAspectsData.aspects : [];
485
+ if (transitAspects.length > 0) {
486
+ transitAspects.forEach(aspect => {
487
+ const birthPlanetData = getAstrologicalData(aspect.birthPlanet, birthData);
488
+ const birthHouse = getPlanetHouse(parseFloat(birthPlanetData.degreeInSign) + (signs.indexOf(birthPlanetData.sign) * 30), houses.house);
489
+
490
+ // Bestimme die Aspekt-Phase
491
+ const aspectAngles = {
492
+ 'Konjunktion': 0,
493
+ 'Opposition': 180,
494
+ 'Quadrat': 90,
495
+ 'Trigon': 120,
496
+ 'Sextil': 60
497
+ };
498
+ const targetAngle = aspectAngles[aspect.type];
499
+ const phase = determineAspectPhase(planetName, aspect.birthPlanet, transitDateDisplay, aspect.type, targetAngle);
500
+
501
+ // Berechne das genaue Datum und die Uhrzeit für diesen Aspekt
502
+ const exactDateTime = findExactAspectTime(planetName, aspect.birthPlanet, aspect.type, transitDateDisplay);
503
+ let dateTimeStr = '-';
504
+ if (exactDateTime) {
505
+ dateTimeStr = `${String(exactDateTime.day).padStart(2, '0')}.${String(exactDateTime.month).padStart(2, '0')}.${exactDateTime.year} ${String(exactDateTime.hour).padStart(2, '0')}:${String(exactDateTime.minute).padStart(2, '0')}`;
506
+ } else if (phase === 'separativ') {
507
+ // Für separative Phasen: Suche das letzte exakte Datum in der Vergangenheit
508
+ const lastExactDateTime = findLastExactAspectTime(planetName, aspect.birthPlanet, aspect.type, transitDateDisplay);
509
+ if (lastExactDateTime) {
510
+ dateTimeStr = `${String(lastExactDateTime.day).padStart(2, '0')}.${String(lastExactDateTime.month).padStart(2, '0')}.${lastExactDateTime.year} ${String(lastExactDateTime.hour).padStart(2, '0')}:${String(lastExactDateTime.minute).padStart(2, '0')} (e)`;
511
+ }
512
+ }
513
+
514
+ const transitPlanetFormatted = planetName.charAt(0).toUpperCase() + planetName.slice(1);
515
+ const aspectTypeFormatted = aspect.type.padEnd(11);
516
+ const aspectPlanetFormatted = aspect.birthPlanet.charAt(0).toUpperCase() + aspect.birthPlanet.slice(1);
517
+ const orbFormatted = aspect.orb.padEnd(4);
518
+ const transitPosFormatted = `${transitPlanetData.sign} ${transitPlanetData.degreeInSign}°`;
519
+ const transitHouseFormatted = transitHouse.toString().padEnd(11);
520
+ const birthHouseFormatted = birthHouse.toString().padEnd(11);
521
+ const phaseFormatted = phase === 'separativ' ? 'separativ'.padEnd(11) : phase.padEnd(11);
522
+ const dateTimeFormatted = dateTimeStr.padEnd(22);
523
+
524
+ console.log(`| ${transitPlanetFormatted.padEnd(10)} | ${aspectTypeFormatted} | ${aspectPlanetFormatted.padEnd(10)} | ${orbFormatted}° | ${transitPosFormatted.padEnd(16)} | ${transitHouseFormatted} | ${birthHouseFormatted} | ${phaseFormatted} | ${dateTimeFormatted} |`);
525
+ });
526
+ }
527
+ }
528
+
529
+ console.log('================================================================================');
530
+ console.log(`\nAnalyse-Datum: ${transitDateDisplay.day}.${transitDateDisplay.month}.${transitDateDisplay.year}`);
531
+ console.log(`Geburtsdatum: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
532
+
533
+ }
534
+
535
+ return;
536
+ }
537
+
538
+ // Persönliche Transit-Aspekte anzeigen, falls --transit-aspekte oder --tr Option angegeben
539
+ if (options.transitAspekte || options.tr) {
540
+ // Geburtsdaten sind für Transit-Aspekte erforderlich
541
+ const birthData = getBirthDataFromConfig();
542
+ if (!birthData) {
543
+ console.error('Fehler: Persönliche Transit-Aspekte erfordern Geburtsdaten. Bitte führe --setup aus, um deine Geburtsdaten zu konfigurieren.');
544
+ process.exit(1);
545
+ }
546
+
547
+ // Benutzerdefiniertes Datum verwenden, falls angegeben
548
+ let transitDate = null;
549
+ if (options.d) {
550
+ // Versuche, das Datum als DD.MM.YYYY oder DD.MM.YYYY HH:MM zu parsen
551
+ const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
552
+ const match = options.d.match(dateRegex);
553
+
554
+ if (match) {
555
+ const day = parseInt(match[1], 10);
556
+ const month = parseInt(match[2], 10);
557
+ const year = parseInt(match[3], 10);
558
+ const hour = match[4] ? parseInt(match[4], 10) : 12; // Standard: 12 Uhr
559
+ const minute = match[5] ? parseInt(match[5], 10) : 0; // Standard: 0 Minuten
560
+
561
+ // Überprüfe, ob das Datum gültig ist
562
+ const date = new Date(year, month - 1, day, hour, minute);
563
+ if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
564
+ transitDate = {
565
+ day: day,
566
+ month: month,
567
+ year: year,
568
+ hour: hour,
569
+ minute: minute
570
+ };
571
+ console.log(`Verwende Transitdatum: ${day}.${month}.${year} ${hour}:${minute.toString().padStart(2, '0')}`);
572
+ } else {
573
+ console.error('Ungültiges Datum:', options.d);
574
+ console.error('💡 Bitte verwende das Format: DD.MM.YYYY oder "DD.MM.YYYY HH:MM" (mit Anführungszeichen für Datum mit Uhrzeit)');
575
+ process.exit(1);
576
+ }
577
+ } else {
578
+ console.error('Ungültiges Datum:', options.d);
579
+ console.error('💡 Bitte verwende das Format: DD.MM.YYYY oder "DD.MM.YYYY HH:MM" (mit Anführungszeichen für Datum mit Uhrzeit)');
580
+ process.exit(1);
581
+ }
582
+ }
583
+
584
+ // Zeige persönliche Transit-Aspekte an
585
+ // Verwende den spezifischen Planeten, falls angegeben, oder alle Planeten
586
+ const targetPlanet = planetArg ? planetArg.toLowerCase() : null;
587
+ showPersonalTransitAspects(transitDate, birthData, targetPlanet);
588
+
589
+ return;
590
+ }
591
+
411
592
  // Aspektfiguren anzeigen, falls --af Option angegeben (ohne Planet erforderlich)
412
593
  if (options.af) {
413
594
  // Geburtsdaten verwenden, falls --ich Option angegeben
@@ -28,10 +28,16 @@ async function analyzeHouseDistribution(planetName, folderPath, houseSystem = 'K
28
28
  const planetLongitude = planetData.longitude;
29
29
 
30
30
  // Berechne die Hausposition für dieses Datum
31
- const julianDay = swisseph.swe_julday(
32
- dateComponents.year, dateComponents.month, dateComponents.day,
33
- dateComponents.hour + dateComponents.minute / 60, swisseph.SE_GREG_CAL
34
- );
31
+ const { calculateJulianDayUTC, getTimezoneOffset, loadConfig } = require('../astrology/astrologyService');
32
+ let tz = null;
33
+ try {
34
+ const config = loadConfig();
35
+ if (config && config.currentLocation && config.currentLocation.timezone) {
36
+ tz = config.currentLocation.timezone;
37
+ }
38
+ } catch (_) {}
39
+ const off = tz ? getTimezoneOffset(dateComponents, tz) : -new Date().getTimezoneOffset();
40
+ const julianDay = calculateJulianDayUTC(dateComponents, off);
35
41
 
36
42
  const houses = await calculateHouses(julianDay, houseSystem);
37
43
  const planetHouse = getPlanetHouse(planetLongitude, houses.house);
@@ -89,10 +95,16 @@ async function filterFilesByHouse(planetName, folderPath, targetHouse, houseSyst
89
95
  const planetLongitude = planetData.longitude;
90
96
 
91
97
  // Berechne die Hausposition für dieses Datum
92
- const julianDay = swisseph.swe_julday(
93
- dateComponents.year, dateComponents.month, dateComponents.day,
94
- dateComponents.hour + dateComponents.minute / 60, swisseph.SE_GREG_CAL
95
- );
98
+ const { calculateJulianDayUTC, getTimezoneOffset, loadConfig } = require('../astrology/astrologyService');
99
+ let tz = null;
100
+ try {
101
+ const config = loadConfig();
102
+ if (config && config.currentLocation && config.currentLocation.timezone) {
103
+ tz = config.currentLocation.timezone;
104
+ }
105
+ } catch (_) {}
106
+ const off = tz ? getTimezoneOffset(dateComponents, tz) : -new Date().getTimezoneOffset();
107
+ const julianDay = calculateJulianDayUTC(dateComponents, off);
96
108
 
97
109
  const houses = await calculateHouses(julianDay, houseSystem);
98
110
  const planetHouse = getPlanetHouse(planetLongitude, houses.house);