Skip to content

Commit b5056ef

Browse files
committed
feat: locationless transits
1 parent 33c99a0 commit b5056ef

14 files changed

Lines changed: 3615 additions & 35 deletions

src/defs/index.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,28 @@ export const houseObject = z
8282
ref: "HouseObject",
8383
});
8484

85-
export const planetPositionObject = z
85+
export const genericPlanetPositionObject = z
8686
.object({
8787
id: planetIdEnum,
8888
name: planetEnum,
8989
longitude: z.number(),
9090
latitude: z.number().optional(),
9191
isRetrograde: z.boolean(),
9292
degree: z.number().min(0).max(30),
93-
houseNumber: z.number().min(1).max(12),
9493
zodiac: zodiacDetailsObject,
9594
})
95+
.openapi({
96+
ref: "GenericPlanetPositionObject",
97+
});
98+
99+
export type GenericPlanetPositionObject = z.infer<
100+
typeof genericPlanetPositionObject
101+
>;
102+
103+
export const planetPositionObject = genericPlanetPositionObject
104+
.extend({
105+
houseNumber: z.number().min(1).max(12),
106+
})
96107
.openapi({
97108
ref: "PlanetPositionObject",
98109
});
@@ -103,6 +114,14 @@ export const aspectEnum = z.nativeEnum(Aspect).openapi({
103114
ref: "Aspect",
104115
});
105116

117+
export const typeOfAspect = z
118+
.enum(["transit-to-natal", "natal-to-natal", "transit-to-transit"])
119+
.openapi({
120+
ref: "TypeOfAspect",
121+
});
122+
123+
export type TypeOfAspect = z.infer<typeof typeOfAspect>;
124+
106125
export const aspectObject = z
107126
.object({
108127
planet1: z.object({
@@ -120,11 +139,7 @@ export const aspectObject = z
120139
name: aspectEnum,
121140
}),
122141
orb: z.number(),
123-
typeOfAspect: z.enum([
124-
"transit-to-natal",
125-
"natal-to-natal",
126-
"transit-to-transit",
127-
]),
142+
typeOfAspect,
128143
})
129144
.openapi({
130145
ref: "AspectObject",

src/defs/responses.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { z } from "zod";
22

33
import {
44
aspectObject,
5+
genericPlanetPositionObject,
56
houseObject,
67
ingressObject,
78
planetEnum,
@@ -52,6 +53,40 @@ export type CalculateDailyTransitsResponse = z.infer<
5253
typeof calculateDailyTransitsResponse
5354
>;
5455

56+
export const calculateGenericChartResponse = z
57+
.object({
58+
signs: z.object({
59+
sun: zodiacSignObject,
60+
moon: zodiacMoonSignObject,
61+
}),
62+
planets: z.array(genericPlanetPositionObject),
63+
aspects: z.array(aspectObject),
64+
declinations: z.array(aspectObject),
65+
})
66+
.openapi({
67+
ref: "CalculateGenericChartResponse",
68+
});
69+
70+
export type CalculateGenericChartResponse = z.infer<
71+
typeof calculateGenericChartResponse
72+
>;
73+
74+
export const calculateGenericTransitChartResponse = z
75+
.object({
76+
chart: calculateGenericChartResponse,
77+
notableEvents: z.object({
78+
retrogradePlanets: z.array(planetEnum),
79+
ingresses: z.array(ingressObject),
80+
}),
81+
})
82+
.openapi({
83+
ref: "CalculateGenericTransitChartResponse",
84+
});
85+
86+
export type CalculateGenericTransitChartResponse = z.infer<
87+
typeof calculateGenericTransitChartResponse
88+
>;
89+
5590
export const errorResponse = z
5691
.object({
5792
success: z.literal(false),

src/index.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import {
1010
baseResponse,
1111
calculateBirthChartResponse,
1212
calculateDailyTransitsResponse,
13+
calculateGenericTransitChartResponse,
1314
errorResponse,
1415
} from "@/defs/responses.ts";
1516
import { calculateBirthChart } from "@/lib/birthCharts/calculateBirthChart.ts";
17+
import { calculateGenericTransitChart } from "@/lib/calculateGenericChart.ts";
1618
import { calculateTransitsForDay } from "@/lib/calculateTransitsForDay.ts";
1719
import { setSwissephPath } from "@/lib/swisseph.ts";
1820

@@ -78,12 +80,14 @@ app.get(
7880
.number()
7981
.min(0)
8082
.max(23)
83+
.default(0)
8184
.optional()
8285
.describe("The UTC hour of birth"),
8386
minute: z.coerce
8487
.number()
8588
.min(0)
8689
.max(59)
90+
.default(0)
8791
.optional()
8892
.describe("The UTC minute of birth"),
8993
latitude: z.coerce
@@ -180,12 +184,14 @@ app.get(
180184
.number()
181185
.min(0)
182186
.max(59)
187+
.default(0)
183188
.optional()
184189
.describe("The UTC minute of birth"),
185190
birthHour: z.coerce
186191
.number()
187192
.min(0)
188193
.max(23)
194+
.default(0)
189195
.optional()
190196
.describe("The UTC hour of birth"),
191197
birthLatitude: z.coerce
@@ -299,6 +305,97 @@ app.get(
299305
},
300306
);
301307

308+
app.get(
309+
"/generic-chart",
310+
describeRoute({
311+
operationId: "calculateGenericTransitChart",
312+
description:
313+
"This endpoint calculates the transits for a given date but does not require a location or birth data.\n\n**Do not use this for personalised horoscopes**",
314+
summary: "Calculate a generic transit chart",
315+
responses: {
316+
200: {
317+
description: "OK",
318+
content: {
319+
"application/json": {
320+
schema: resolver(
321+
baseResponse.extend({
322+
data: calculateGenericTransitChartResponse,
323+
}),
324+
),
325+
},
326+
},
327+
},
328+
400: {
329+
description: "User Error",
330+
content: {
331+
"application/json": {
332+
schema: resolver(errorResponse),
333+
},
334+
},
335+
},
336+
},
337+
}),
338+
validator(
339+
"query",
340+
z.object({
341+
year: z.coerce
342+
.number()
343+
.min(1900)
344+
.max(2100)
345+
.describe("The UTC year to calculate transits for"),
346+
month: z.coerce
347+
.number()
348+
.min(1)
349+
.max(12)
350+
.describe(
351+
"The UTC month to calculate transits for. NOT zero-indexed",
352+
),
353+
day: z.coerce
354+
.number()
355+
.min(1)
356+
.max(31)
357+
.describe("The UTC day to calculate transits for"),
358+
hour: z.coerce
359+
.number()
360+
.min(0)
361+
.max(23)
362+
.default(12)
363+
.optional()
364+
.describe("The UTC hour to calculate transits for"),
365+
minute: z.coerce
366+
.number()
367+
.min(0)
368+
.max(59)
369+
.default(0)
370+
.optional()
371+
.describe("The UTC minute to calculate transits for"),
372+
}),
373+
),
374+
async (c) => {
375+
const { year, month, day, hour, minute } = c.req.valid("query");
376+
377+
const transitDate = new Date(
378+
Date.UTC(year, month - 1, day, hour ?? 12, minute ?? 0),
379+
);
380+
381+
const transits = calculateGenericTransitChart(transitDate);
382+
383+
if (transits.isErr()) {
384+
return c.json(
385+
{
386+
error: transits.error,
387+
},
388+
500,
389+
);
390+
}
391+
392+
return c.json({
393+
success: true,
394+
data: transits.value,
395+
});
396+
},
397+
);
398+
302399
const openapiServers = [
303400
{
304401
description: "Production server",

src/lib/birthCharts/calculateBirthChart.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { calculateHouses } from "@/lib/birthCharts/calculateHouses.ts";
66
import { getAnglesForDate } from "@/lib/calculateAngles.ts";
77
import { getAspectsForDate } from "@/lib/calculateAspects.ts";
88
import { getDeclinationsForDate } from "@/lib/calculateDeclinations.ts";
9-
import { getPlanetaryPositionsForDate } from "@/lib/calculatePlanetPositions.ts";
9+
import { getPlanetaryPositionsForDateAndLocation } from "@/lib/calculatePlanetPositions.ts";
1010
import { calculateSigns } from "@/lib/calculateSigns.ts";
1111
import { rad } from "@/lib/degrees.ts";
1212

@@ -36,7 +36,7 @@ export function calculateBirthChart(
3636
return houses;
3737
}
3838

39-
const planetPositions = getPlanetaryPositionsForDate(
39+
const planetPositions = getPlanetaryPositionsForDateAndLocation(
4040
jde,
4141
latAngle,
4242
lonAngle,

src/lib/calculateAspects.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import * as astronomia from "astronomia";
22
import { Err, Result, ok } from "neverthrow";
33

4-
import type { AspectObject } from "@/defs";
4+
import type { AspectObject, TypeOfAspect } from "@/defs";
55
import { Aspect, Planet, PlanetId } from "@/defs/enums.ts";
66
import { getAnglesForDate } from "@/lib/calculateAngles.ts";
7-
import { getPlanetaryPositionsForDate } from "@/lib/calculatePlanetPositions.ts";
7+
import { getPlanetaryPositionsForDateAndLocation } from "@/lib/calculatePlanetPositions.ts";
88
import type { ExtractError } from "@/types/neverthrow.ts";
99

1010
const ASPECTS = [
@@ -23,6 +23,7 @@ const ASPECTS = [
2323

2424
export function computeAspects(
2525
celestialBodies: { id: PlanetId; name: Planet; longitude: number }[],
26+
typeOfAspect: TypeOfAspect = "natal-to-natal",
2627
) {
2728
const aspects: AspectObject[] = [];
2829

@@ -44,7 +45,7 @@ export function computeAspects(
4445
planet2: { id: body2.id, name: body2.name },
4546
aspect: { id: aspect.id, name: aspect.name },
4647
orb: orb,
47-
typeOfAspect: "natal-to-natal",
48+
typeOfAspect,
4849
});
4950
break;
5051
}
@@ -100,7 +101,11 @@ export function getAspectsForDate(
100101
lat: astronomia.sexagesimal.Angle,
101102
lon: astronomia.sexagesimal.Angle,
102103
) {
103-
const planetResults = getPlanetaryPositionsForDate(jde, lat, lon);
104+
const planetResults = getPlanetaryPositionsForDateAndLocation(
105+
jde,
106+
lat,
107+
lon,
108+
);
104109
const angleResults = getAnglesForDate(jde, lat, lon);
105110

106111
if (planetResults.isErr() || angleResults.isErr()) {

0 commit comments

Comments
 (0)