Spaces:
Sleeping
Sleeping
Upload 14 files
Browse files- .gitattributes +4 -0
- Pasted-C-Configurar-o-caminho-para-os-arquivos-de-efem-rides-ajuste-conforme-necess-rio-swe-set-ephe-pa-1740106355605.txt +185 -0
- Pasted-import-swisseph-as-swe-from-datetime-import-datetime-import-pytz-from-geopy-geocoders-import-Nominat-1740106507168.txt +194 -0
- app.py +216 -0
- chart_generator.py +193 -0
- custom.css +237 -0
- generated-icon.png +3 -0
- pyproject.toml +17 -0
- requirements.txt +9 -0
- seas_18.se1 +3 -0
- semo_18.se1 +3 -0
- sepl_18.se1 +3 -0
- streamlit.service +12 -0
- utils.py +97 -0
- uv.lock +0 -0
.gitattributes
CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
generated-icon.png filter=lfs diff=lfs merge=lfs -text
|
37 |
+
seas_18.se1 filter=lfs diff=lfs merge=lfs -text
|
38 |
+
semo_18.se1 filter=lfs diff=lfs merge=lfs -text
|
39 |
+
sepl_18.se1 filter=lfs diff=lfs merge=lfs -text
|
Pasted-C-Configurar-o-caminho-para-os-arquivos-de-efem-rides-ajuste-conforme-necess-rio-swe-set-ephe-pa-1740106355605.txt
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
C
|
2 |
+
# Configurar o caminho para os arquivos de efemérides (ajuste conforme necessário)
|
3 |
+
swe.set_ephe_path('/path/to/ephe') # Substitua pelo caminho correto no seu sistema
|
4 |
+
|
5 |
+
def download_ephe_files():
|
6 |
+
"""Baixa e configura os arquivos de efemérides necessários."""
|
7 |
+
# Criar diretório para as efemérides se não existir
|
8 |
+
ephe_dir = Path("ephe")
|
9 |
+
ephe_dir.mkdir(exist_ok=True)
|
10 |
+
|
11 |
+
# URL base do repositório
|
12 |
+
base_url = "https://raw.githubusercontent.com/aloistr/swisseph/master/ephe/"
|
13 |
+
|
14 |
+
# Lista de arquivos essenciais
|
15 |
+
essential_files = [
|
16 |
+
"seas_18.se1", # Arquivo principal do Sol
|
17 |
+
"semo_18.se1", # Arquivo principal da Lua
|
18 |
+
"sepl_18.se1" # Arquivo principal dos planetas
|
19 |
+
]
|
20 |
+
|
21 |
+
# Baixar arquivos
|
22 |
+
for filename in essential_files:
|
23 |
+
file_path = ephe_dir / filename
|
24 |
+
if not file_path.exists():
|
25 |
+
print(f"Baixando {filename}...")
|
26 |
+
try:
|
27 |
+
response = requests.get(f"{base_url}{filename}")
|
28 |
+
response.raise_for_status()
|
29 |
+
with open(file_path, "wb") as f:
|
30 |
+
f.write(response.content)
|
31 |
+
except Exception as e:
|
32 |
+
print(f"Erro ao baixar {filename}: {e}")
|
33 |
+
return False
|
34 |
+
|
35 |
+
# Configurar o caminho das efemérides
|
36 |
+
swe.set_ephe_path(str(ephe_dir))
|
37 |
+
return True
|
38 |
+
|
39 |
+
# Funções auxiliares
|
40 |
+
def get_timezone(cidade):
|
41 |
+
"""Obtém latitude, longitude e fuso horário a partir do nome da cidade."""
|
42 |
+
geolocator = Nominatim(user_agent="astro_calculator")
|
43 |
+
location = geolocator.geocode(cidade)
|
44 |
+
if not location:
|
45 |
+
raise ValueError("Cidade não encontrada.")
|
46 |
+
lat, lon = location.latitude, location.longitude
|
47 |
+
tf = TimezoneFinder()
|
48 |
+
timezone_str = tf.timezone_at(lat=lat, lng=lon)
|
49 |
+
if not timezone_str:
|
50 |
+
raise ValueError("Fuso horário não encontrado para esta localização.")
|
51 |
+
return timezone_str, lat, lon
|
52 |
+
|
53 |
+
def get_julian_day(data_str, hora_str, timezone_str):
|
54 |
+
"""Converte data e hora local para Julian Day (UTC)."""
|
55 |
+
try:
|
56 |
+
# Parsear data e hora
|
57 |
+
dia, mes, ano = map(int, data_str.split('/'))
|
58 |
+
hora, minuto = map(int, hora_str.split(':'))
|
59 |
+
# Criar objeto datetime com fuso horário local
|
60 |
+
tz = pytz.timezone(timezone_str)
|
61 |
+
dt_local = tz.localize(datetime(ano, mes, dia, hora, minuto))
|
62 |
+
# Converter para UTC
|
63 |
+
dt_utc = dt_local.astimezone(pytz.UTC)
|
64 |
+
# Calcular Julian Day
|
65 |
+
return swe.utc_to_jd(dt_utc.year, dt_utc.month, dt_utc.day,
|
66 |
+
dt_utc.hour, dt_utc.minute, dt_utc.second,
|
67 |
+
swe.GREG_CAL)[1]
|
68 |
+
except Exception as e:
|
69 |
+
print(f"Erro ao converter data/hora: {e}")
|
70 |
+
return None
|
71 |
+
|
72 |
+
def calcular_signo(graus):
|
73 |
+
"""Converte uma posição em graus para o signo correspondente."""
|
74 |
+
signos = ["Áries", "Touro", "Gêmeos", "Câncer", "Leão", "Virgem",
|
75 |
+
"Libra", "Escorpião", "Sagitário", "Capricórnio", "Aquário", "Peixes"]
|
76 |
+
grau_normalizado = graus % 360
|
77 |
+
indice = int(grau_normalizado // 30)
|
78 |
+
return signos[indice]
|
79 |
+
|
80 |
+
def calcular_aspectos(posicoes):
|
81 |
+
"""Calcula aspectos principais entre os planetas."""
|
82 |
+
aspectos = []
|
83 |
+
orbes = {"Conjunção": 8, "Oposição": 8, "Trígono": 6, "Quadrratura": 6, "Sextil": 4}
|
84 |
+
tipos_aspectos = {
|
85 |
+
0: "Conjunção", 60: "Sextil", 90: "Quadrratura", 120: "Trígono", 180: "Oposição"
|
86 |
+
}
|
87 |
+
|
88 |
+
planetas_lista = list(posicoes.keys())
|
89 |
+
for i in range(len(planetas_lista)):
|
90 |
+
for j in range(i + 1, len(planetas_lista)):
|
91 |
+
p1, p2 = planetas_lista[i], planetas_lista[j]
|
92 |
+
grau1, grau2 = posicoes[p1]["graus"], posicoes[p2]["graus"]
|
93 |
+
diff = min((grau1 - grau2) % 360, (grau2 - grau1) % 360)
|
94 |
+
|
95 |
+
for angulo, tipo in tipos_aspectos.items():
|
96 |
+
if abs(diff - angulo) <= orbes[tipo]:
|
97 |
+
aspectos.append(f"{p1} em {tipo} com {p2} ({diff:.2f}°)")
|
98 |
+
|
99 |
+
return aspectos
|
100 |
+
|
101 |
+
# Programa principal
|
102 |
+
def main():
|
103 |
+
print("Bem-vindo ao cálculo do Mapa Astral!")
|
104 |
+
|
105 |
+
# Adicionar verificação e download dos arquivos de efemérides
|
106 |
+
print("Verificando arquivos de efemérides...")
|
107 |
+
if not download_ephe_files():
|
108 |
+
print("Erro ao configurar os arquivos de efemérides. O programa não pode continuar.")
|
109 |
+
return
|
110 |
+
|
111 |
+
data_nascimento = input("Digite sua data de nascimento (dd/mm/aaaa): ")
|
112 |
+
hora_nascimento = input("Digite sua hora de nascimento (hh:mm): ")
|
113 |
+
cidade_nascimento = input("Digite a cidade e país de nascimento (ex.: São Paulo, Brasil): ")
|
114 |
+
|
115 |
+
try:
|
116 |
+
# Obter fuso horário e coordenadas
|
117 |
+
timezone, latitude, longitude = get_timezone(cidade_nascimento)
|
118 |
+
print(f"\nLocal: {cidade_nascimento} (Lat: {latitude:.4f}, Lon: {longitude:.4f})")
|
119 |
+
print(f"Fuso horário: {timezone}")
|
120 |
+
|
121 |
+
# Converter para Julian Day
|
122 |
+
julian_day = get_julian_day(data_nascimento, hora_nascimento, timezone)
|
123 |
+
if julian_day is None:
|
124 |
+
raise ValueError("Erro ao calcular o Julian Day.")
|
125 |
+
|
126 |
+
# Calcular posições planetárias e nodos
|
127 |
+
planetas = {
|
128 |
+
"Sol": swe.SUN, "Lua": swe.MOON, "Mercúrio": swe.MERCURY,
|
129 |
+
"Vênus": swe.VENUS, "Marte": swe.MARS, "Júpiter": swe.JUPITER,
|
130 |
+
"Saturno": swe.SATURN, "Urano": swe.URANUS, "Netuno": swe.NEPTUNE,
|
131 |
+
"Plutão": swe.PLUTO, "Nodo Norte": swe.MEAN_NODE
|
132 |
+
}
|
133 |
+
posicoes = {}
|
134 |
+
for nome, planeta in planetas.items():
|
135 |
+
resultado = swe.calc_ut(julian_day, planeta)
|
136 |
+
pos = resultado[0][0] # Pegando apenas a longitude eclíptica
|
137 |
+
posicoes[nome] = {"graus": pos, "signo": calcular_signo(pos)}
|
138 |
+
|
139 |
+
# Nodo Sul é oposto ao Nodo Norte
|
140 |
+
nodo_sul_graus = (posicoes["Nodo Norte"]["graus"] + 180) % 360
|
141 |
+
posicoes["Nodo Sul"] = {"graus": nodo_sul_graus, "signo": calcular_signo(nodo_sul_graus)}
|
142 |
+
|
143 |
+
# Calcular Ascendente e casas
|
144 |
+
casas, aspectos_casas = swe.houses(julian_day, latitude, longitude, b'P') # Placidus
|
145 |
+
ascendente = casas[0]
|
146 |
+
signo_ascendente = calcular_signo(ascendente)
|
147 |
+
meio_do_ceu = casas[9]
|
148 |
+
signo_mc = calcular_signo(meio_do_ceu)
|
149 |
+
|
150 |
+
# Calcular aspectos
|
151 |
+
aspectos = calcular_aspectos(posicoes)
|
152 |
+
|
153 |
+
# Exibir resultados
|
154 |
+
print("\n=== Mapa Astral ===")
|
155 |
+
print(f"Signo Solar: {posicoes['Sol']['signo']} ({posicoes['Sol']['graus']:.2f}°)")
|
156 |
+
print(f"Signo Lunar: {posicoes['Lua']['signo']} ({posicoes['Lua']['graus']:.2f}°)")
|
157 |
+
print(f"Ascendente: {signo_ascendente} ({ascendente:.2f}°)")
|
158 |
+
print(f"Meio do Céu: {signo_mc} ({meio_do_ceu:.2f}°)")
|
159 |
+
|
160 |
+
print("\n=== Outros Planetas ===")
|
161 |
+
for planeta, info in posicoes.items():
|
162 |
+
if planeta not in ["Sol", "Lua", "Nodo Norte", "Nodo Sul"]:
|
163 |
+
print(f"{planeta}: {info['signo']} ({info['graus']:.2f}°)")
|
164 |
+
|
165 |
+
print("\n=== Nodos Lunares ===")
|
166 |
+
print(f"Nodo Norte: {posicoes['Nodo Norte']['signo']} ({posicoes['Nodo Norte']['graus']:.2f}°)")
|
167 |
+
print(f"Nodo Sul: {posicoes['Nodo Sul']['signo']} ({posicoes['Nodo Sul']['graus']:.2f}°)")
|
168 |
+
|
169 |
+
print("\n=== Casas Astrológicas ===")
|
170 |
+
for i, casa in enumerate(casas[:12], 1):
|
171 |
+
signo_casa = calcular_signo(casa)
|
172 |
+
print(f"Casa {i}: {signo_casa} ({casa:.2f}°)")
|
173 |
+
|
174 |
+
print("\n=== Aspectos Principais ===")
|
175 |
+
if aspectos:
|
176 |
+
for aspecto in aspectos:
|
177 |
+
print(aspecto)
|
178 |
+
else:
|
179 |
+
print("Nenhum aspecto significativo encontrado.")
|
180 |
+
|
181 |
+
except Exception as e:
|
182 |
+
print(f"Erro: {e}")
|
183 |
+
|
184 |
+
if __name__ == "__main__":
|
185 |
+
main()
|
Pasted-import-swisseph-as-swe-from-datetime-import-datetime-import-pytz-from-geopy-geocoders-import-Nominat-1740106507168.txt
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import swisseph as swe
|
2 |
+
from datetime import datetime
|
3 |
+
import pytz
|
4 |
+
from geopy.geocoders import Nominatim
|
5 |
+
from timezonefinder import TimezoneFinder
|
6 |
+
import os
|
7 |
+
import requests
|
8 |
+
import zipfile
|
9 |
+
from pathlib import Path
|
10 |
+
|
11 |
+
# Configurar o caminho para os arquivos de efemérides (ajuste conforme necessário)
|
12 |
+
swe.set_ephe_path('/path/to/ephe') # Substitua pelo caminho correto no seu sistema
|
13 |
+
|
14 |
+
def download_ephe_files():
|
15 |
+
"""Baixa e configura os arquivos de efemérides necessários."""
|
16 |
+
# Criar diretório para as efemérides se não existir
|
17 |
+
ephe_dir = Path("ephe")
|
18 |
+
ephe_dir.mkdir(exist_ok=True)
|
19 |
+
|
20 |
+
# URL base do repositório
|
21 |
+
base_url = "https://raw.githubusercontent.com/aloistr/swisseph/master/ephe/"
|
22 |
+
|
23 |
+
# Lista de arquivos essenciais
|
24 |
+
essential_files = [
|
25 |
+
"seas_18.se1", # Arquivo principal do Sol
|
26 |
+
"semo_18.se1", # Arquivo principal da Lua
|
27 |
+
"sepl_18.se1" # Arquivo principal dos planetas
|
28 |
+
]
|
29 |
+
|
30 |
+
# Baixar arquivos
|
31 |
+
for filename in essential_files:
|
32 |
+
file_path = ephe_dir / filename
|
33 |
+
if not file_path.exists():
|
34 |
+
print(f"Baixando {filename}...")
|
35 |
+
try:
|
36 |
+
response = requests.get(f"{base_url}{filename}")
|
37 |
+
response.raise_for_status()
|
38 |
+
with open(file_path, "wb") as f:
|
39 |
+
f.write(response.content)
|
40 |
+
except Exception as e:
|
41 |
+
print(f"Erro ao baixar {filename}: {e}")
|
42 |
+
return False
|
43 |
+
|
44 |
+
# Configurar o caminho das efemérides
|
45 |
+
swe.set_ephe_path(str(ephe_dir))
|
46 |
+
return True
|
47 |
+
|
48 |
+
# Funções auxiliares
|
49 |
+
def get_timezone(cidade):
|
50 |
+
"""Obtém latitude, longitude e fuso horário a partir do nome da cidade."""
|
51 |
+
geolocator = Nominatim(user_agent="astro_calculator")
|
52 |
+
location = geolocator.geocode(cidade)
|
53 |
+
if not location:
|
54 |
+
raise ValueError("Cidade não encontrada.")
|
55 |
+
lat, lon = location.latitude, location.longitude
|
56 |
+
tf = TimezoneFinder()
|
57 |
+
timezone_str = tf.timezone_at(lat=lat, lng=lon)
|
58 |
+
if not timezone_str:
|
59 |
+
raise ValueError("Fuso horário não encontrado para esta localização.")
|
60 |
+
return timezone_str, lat, lon
|
61 |
+
|
62 |
+
def get_julian_day(data_str, hora_str, timezone_str):
|
63 |
+
"""Converte data e hora local para Julian Day (UTC)."""
|
64 |
+
try:
|
65 |
+
# Parsear data e hora
|
66 |
+
dia, mes, ano = map(int, data_str.split('/'))
|
67 |
+
hora, minuto = map(int, hora_str.split(':'))
|
68 |
+
# Criar objeto datetime com fuso horário local
|
69 |
+
tz = pytz.timezone(timezone_str)
|
70 |
+
dt_local = tz.localize(datetime(ano, mes, dia, hora, minuto))
|
71 |
+
# Converter para UTC
|
72 |
+
dt_utc = dt_local.astimezone(pytz.UTC)
|
73 |
+
# Calcular Julian Day
|
74 |
+
return swe.utc_to_jd(dt_utc.year, dt_utc.month, dt_utc.day,
|
75 |
+
dt_utc.hour, dt_utc.minute, dt_utc.second,
|
76 |
+
swe.GREG_CAL)[1]
|
77 |
+
except Exception as e:
|
78 |
+
print(f"Erro ao converter data/hora: {e}")
|
79 |
+
return None
|
80 |
+
|
81 |
+
def calcular_signo(graus):
|
82 |
+
"""Converte uma posição em graus para o signo correspondente."""
|
83 |
+
signos = ["Áries", "Touro", "Gêmeos", "Câncer", "Leão", "Virgem",
|
84 |
+
"Libra", "Escorpião", "Sagitário", "Capricórnio", "Aquário", "Peixes"]
|
85 |
+
grau_normalizado = graus % 360
|
86 |
+
indice = int(grau_normalizado // 30)
|
87 |
+
return signos[indice]
|
88 |
+
|
89 |
+
def calcular_aspectos(posicoes):
|
90 |
+
"""Calcula aspectos principais entre os planetas."""
|
91 |
+
aspectos = []
|
92 |
+
orbes = {"Conjunção": 8, "Oposição": 8, "Trígono": 6, "Quadrratura": 6, "Sextil": 4}
|
93 |
+
tipos_aspectos = {
|
94 |
+
0: "Conjunção", 60: "Sextil", 90: "Quadrratura", 120: "Trígono", 180: "Oposição"
|
95 |
+
}
|
96 |
+
|
97 |
+
planetas_lista = list(posicoes.keys())
|
98 |
+
for i in range(len(planetas_lista)):
|
99 |
+
for j in range(i + 1, len(planetas_lista)):
|
100 |
+
p1, p2 = planetas_lista[i], planetas_lista[j]
|
101 |
+
grau1, grau2 = posicoes[p1]["graus"], posicoes[p2]["graus"]
|
102 |
+
diff = min((grau1 - grau2) % 360, (grau2 - grau1) % 360)
|
103 |
+
|
104 |
+
for angulo, tipo in tipos_aspectos.items():
|
105 |
+
if abs(diff - angulo) <= orbes[tipo]:
|
106 |
+
aspectos.append(f"{p1} em {tipo} com {p2} ({diff:.2f}°)")
|
107 |
+
|
108 |
+
return aspectos
|
109 |
+
|
110 |
+
# Programa principal
|
111 |
+
def main():
|
112 |
+
print("Bem-vindo ao cálculo do Mapa Astral!")
|
113 |
+
|
114 |
+
# Adicionar verificação e download dos arquivos de efemérides
|
115 |
+
print("Verificando arquivos de efemérides...")
|
116 |
+
if not download_ephe_files():
|
117 |
+
print("Erro ao configurar os arquivos de efemérides. O programa não pode continuar.")
|
118 |
+
return
|
119 |
+
|
120 |
+
data_nascimento = input("Digite sua data de nascimento (dd/mm/aaaa): ")
|
121 |
+
hora_nascimento = input("Digite sua hora de nascimento (hh:mm): ")
|
122 |
+
cidade_nascimento = input("Digite a cidade e país de nascimento (ex.: São Paulo, Brasil): ")
|
123 |
+
|
124 |
+
try:
|
125 |
+
# Obter fuso horário e coordenadas
|
126 |
+
timezone, latitude, longitude = get_timezone(cidade_nascimento)
|
127 |
+
print(f"\nLocal: {cidade_nascimento} (Lat: {latitude:.4f}, Lon: {longitude:.4f})")
|
128 |
+
print(f"Fuso horário: {timezone}")
|
129 |
+
|
130 |
+
# Converter para Julian Day
|
131 |
+
julian_day = get_julian_day(data_nascimento, hora_nascimento, timezone)
|
132 |
+
if julian_day is None:
|
133 |
+
raise ValueError("Erro ao calcular o Julian Day.")
|
134 |
+
|
135 |
+
# Calcular posições planetárias e nodos
|
136 |
+
planetas = {
|
137 |
+
"Sol": swe.SUN, "Lua": swe.MOON, "Mercúrio": swe.MERCURY,
|
138 |
+
"Vênus": swe.VENUS, "Marte": swe.MARS, "Júpiter": swe.JUPITER,
|
139 |
+
"Saturno": swe.SATURN, "Urano": swe.URANUS, "Netuno": swe.NEPTUNE,
|
140 |
+
"Plutão": swe.PLUTO, "Nodo Norte": swe.MEAN_NODE
|
141 |
+
}
|
142 |
+
posicoes = {}
|
143 |
+
for nome, planeta in planetas.items():
|
144 |
+
resultado = swe.calc_ut(julian_day, planeta)
|
145 |
+
pos = resultado[0][0] # Pegando apenas a longitude eclíptica
|
146 |
+
posicoes[nome] = {"graus": pos, "signo": calcular_signo(pos)}
|
147 |
+
|
148 |
+
# Nodo Sul é oposto ao Nodo Norte
|
149 |
+
nodo_sul_graus = (posicoes["Nodo Norte"]["graus"] + 180) % 360
|
150 |
+
posicoes["Nodo Sul"] = {"graus": nodo_sul_graus, "signo": calcular_signo(nodo_sul_graus)}
|
151 |
+
|
152 |
+
# Calcular Ascendente e casas
|
153 |
+
casas, aspectos_casas = swe.houses(julian_day, latitude, longitude, b'P') # Placidus
|
154 |
+
ascendente = casas[0]
|
155 |
+
signo_ascendente = calcular_signo(ascendente)
|
156 |
+
meio_do_ceu = casas[9]
|
157 |
+
signo_mc = calcular_signo(meio_do_ceu)
|
158 |
+
|
159 |
+
# Calcular aspectos
|
160 |
+
aspectos = calcular_aspectos(posicoes)
|
161 |
+
|
162 |
+
# Exibir resultados
|
163 |
+
print("\n=== Mapa Astral ===")
|
164 |
+
print(f"Signo Solar: {posicoes['Sol']['signo']} ({posicoes['Sol']['graus']:.2f}°)")
|
165 |
+
print(f"Signo Lunar: {posicoes['Lua']['signo']} ({posicoes['Lua']['graus']:.2f}°)")
|
166 |
+
print(f"Ascendente: {signo_ascendente} ({ascendente:.2f}°)")
|
167 |
+
print(f"Meio do Céu: {signo_mc} ({meio_do_ceu:.2f}°)")
|
168 |
+
|
169 |
+
print("\n=== Outros Planetas ===")
|
170 |
+
for planeta, info in posicoes.items():
|
171 |
+
if planeta not in ["Sol", "Lua", "Nodo Norte", "Nodo Sul"]:
|
172 |
+
print(f"{planeta}: {info['signo']} ({info['graus']:.2f}°)")
|
173 |
+
|
174 |
+
print("\n=== Nodos Lunares ===")
|
175 |
+
print(f"Nodo Norte: {posicoes['Nodo Norte']['signo']} ({posicoes['Nodo Norte']['graus']:.2f}°)")
|
176 |
+
print(f"Nodo Sul: {posicoes['Nodo Sul']['signo']} ({posicoes['Nodo Sul']['graus']:.2f}°)")
|
177 |
+
|
178 |
+
print("\n=== Casas Astrológicas ===")
|
179 |
+
for i, casa in enumerate(casas[:12], 1):
|
180 |
+
signo_casa = calcular_signo(casa)
|
181 |
+
print(f"Casa {i}: {signo_casa} ({casa:.2f}°)")
|
182 |
+
|
183 |
+
print("\n=== Aspectos Principais ===")
|
184 |
+
if aspectos:
|
185 |
+
for aspecto in aspectos:
|
186 |
+
print(aspecto)
|
187 |
+
else:
|
188 |
+
print("Nenhum aspecto significativo encontrado.")
|
189 |
+
|
190 |
+
except Exception as e:
|
191 |
+
print(f"Erro: {e}")
|
192 |
+
|
193 |
+
if __name__ == "__main__":
|
194 |
+
main()
|
app.py
ADDED
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import datetime
|
3 |
+
from utils import (
|
4 |
+
download_ephe_files, get_location_data, calculate_julian_day,
|
5 |
+
get_planet_positions, calculate_houses
|
6 |
+
)
|
7 |
+
from chart_generator import create_wheel_chart, PLANET_NAMES
|
8 |
+
|
9 |
+
# Zodiac sign symbols and names
|
10 |
+
ZODIAC_SYMBOLS = {
|
11 |
+
"Aries": "♈",
|
12 |
+
"Taurus": "♉",
|
13 |
+
"Gemini": "♊",
|
14 |
+
"Cancer": "♋",
|
15 |
+
"Leo": "♌",
|
16 |
+
"Virgo": "♍",
|
17 |
+
"Libra": "♎",
|
18 |
+
"Scorpio": "♏",
|
19 |
+
"Sagittarius": "♐",
|
20 |
+
"Capricorn": "♑",
|
21 |
+
"Aquarius": "♒",
|
22 |
+
"Pisces": "♓"
|
23 |
+
}
|
24 |
+
|
25 |
+
def calcular_signo(longitude):
|
26 |
+
signo = ""
|
27 |
+
if 0 <= longitude < 30:
|
28 |
+
signo = "Aries"
|
29 |
+
elif 30 <= longitude < 60:
|
30 |
+
signo = "Taurus"
|
31 |
+
elif 60 <= longitude < 90:
|
32 |
+
signo = "Gemini"
|
33 |
+
elif 90 <= longitude < 120:
|
34 |
+
signo = "Cancer"
|
35 |
+
elif 120 <= longitude < 150:
|
36 |
+
signo = "Leo"
|
37 |
+
elif 150 <= longitude < 180:
|
38 |
+
signo = "Virgo"
|
39 |
+
elif 180 <= longitude < 210:
|
40 |
+
signo = "Libra"
|
41 |
+
elif 210 <= longitude < 240:
|
42 |
+
signo = "Scorpio"
|
43 |
+
elif 240 <= longitude < 270:
|
44 |
+
signo = "Sagittarius"
|
45 |
+
elif 270 <= longitude < 300:
|
46 |
+
signo = "Capricorn"
|
47 |
+
elif 300 <= longitude < 330:
|
48 |
+
signo = "Aquarius"
|
49 |
+
elif 330 <= longitude < 360:
|
50 |
+
signo = "Pisces"
|
51 |
+
return signo
|
52 |
+
|
53 |
+
# Configuração da página
|
54 |
+
st.set_page_config(
|
55 |
+
page_title="Visualizador de Mapa Astral",
|
56 |
+
page_icon="🌟",
|
57 |
+
layout="centered"
|
58 |
+
)
|
59 |
+
|
60 |
+
# Carregar CSS personalizado
|
61 |
+
with open("styles/custom.css") as f:
|
62 |
+
st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
|
63 |
+
|
64 |
+
# Inicializar arquivos de efemérides
|
65 |
+
if not download_ephe_files():
|
66 |
+
st.error("Falha ao inicializar dados astronômicos. Por favor, tente novamente.")
|
67 |
+
st.stop()
|
68 |
+
|
69 |
+
# Título e descrição
|
70 |
+
st.markdown("""
|
71 |
+
<div style='text-align: center; padding: 2rem 0;'>
|
72 |
+
<h1 class='glow-text'>✨ Visualizador de Mapa Astral ✨</h1>
|
73 |
+
</div>
|
74 |
+
""", unsafe_allow_html=True)
|
75 |
+
|
76 |
+
st.markdown("""
|
77 |
+
<div class='cosmic-card'>
|
78 |
+
<p style='text-align: center; font-size: 1.2rem;'>
|
79 |
+
Descubra seu mapa astral através deste visualizador interativo.<br>
|
80 |
+
Insira seus dados de nascimento abaixo para revelar as posições celestiais no momento do seu nascimento.
|
81 |
+
</p>
|
82 |
+
</div>
|
83 |
+
""", unsafe_allow_html=True)
|
84 |
+
|
85 |
+
# Formulário de entrada
|
86 |
+
st.markdown("<div class='cosmic-card'>", unsafe_allow_html=True)
|
87 |
+
if st.viewport_width < 768:
|
88 |
+
# Layout em uma coluna para dispositivos móveis
|
89 |
+
birth_date = st.date_input(
|
90 |
+
"Data de Nascimento",
|
91 |
+
min_value=datetime.date(1900, 1, 1),
|
92 |
+
max_value=datetime.date.today(),
|
93 |
+
help="Selecione sua data de nascimento"
|
94 |
+
)
|
95 |
+
|
96 |
+
birth_time = st.time_input(
|
97 |
+
"Hora de Nascimento",
|
98 |
+
help="Insira o horário mais preciso possível"
|
99 |
+
)
|
100 |
+
|
101 |
+
birth_place = st.text_input(
|
102 |
+
"Local de Nascimento",
|
103 |
+
placeholder="Ex: São Paulo, Brasil",
|
104 |
+
help="Digite a cidade e país de nascimento"
|
105 |
+
)
|
106 |
+
else:
|
107 |
+
# Layout em três colunas para desktop
|
108 |
+
col1, col2, col3 = st.columns(3)
|
109 |
+
with col1:
|
110 |
+
birth_date = st.date_input(
|
111 |
+
"Data de Nascimento",
|
112 |
+
min_value=datetime.date(1900, 1, 1),
|
113 |
+
max_value=datetime.date.today(),
|
114 |
+
help="Selecione sua data de nascimento"
|
115 |
+
)
|
116 |
+
|
117 |
+
with col2:
|
118 |
+
birth_time = st.time_input(
|
119 |
+
"Hora de Nascimento",
|
120 |
+
help="Insira o horário mais preciso possível"
|
121 |
+
)
|
122 |
+
|
123 |
+
with col3:
|
124 |
+
birth_place = st.text_input(
|
125 |
+
"Local de Nascimento",
|
126 |
+
placeholder="Ex: São Paulo, Brasil",
|
127 |
+
help="Digite a cidade e país de nascimento"
|
128 |
+
)
|
129 |
+
|
130 |
+
if st.button("✨ Gerar Mapa Astral ✨", key="generate"):
|
131 |
+
try:
|
132 |
+
with st.spinner("🌟 Calculando posições celestiais..."):
|
133 |
+
location_data = get_location_data(birth_place)
|
134 |
+
jd = calculate_julian_day(birth_date, birth_time, location_data['timezone'])
|
135 |
+
planet_positions = get_planet_positions(jd)
|
136 |
+
houses = calculate_houses(
|
137 |
+
jd,
|
138 |
+
location_data['latitude'],
|
139 |
+
location_data['longitude']
|
140 |
+
)
|
141 |
+
|
142 |
+
# Calcular signo solar
|
143 |
+
signo_solar = calcular_signo(planet_positions['Sun']['longitude'])
|
144 |
+
simbolo_solar = ZODIAC_SYMBOLS[signo_solar]
|
145 |
+
|
146 |
+
st.markdown("<div style='text-align: center;'><h2 class='glow-text'>Seu Mapa Astral</h2></div>", unsafe_allow_html=True)
|
147 |
+
|
148 |
+
# Ajuste do layout responsivo para o resultado
|
149 |
+
if st.viewport_width < 768:
|
150 |
+
# Layout em uma coluna para mobile
|
151 |
+
st.markdown(f"""
|
152 |
+
<div class='cosmic-card signo-solar-card'>
|
153 |
+
<div class='signo-symbol'>{simbolo_solar}</div>
|
154 |
+
<h2 class='signo-nome'>{signo_solar}</h2>
|
155 |
+
<p class='signo-grau'>{planet_positions['Sun']['longitude']:.2f}°</p>
|
156 |
+
</div>
|
157 |
+
""", unsafe_allow_html=True)
|
158 |
+
|
159 |
+
st.markdown("<div class='chart-container'>", unsafe_allow_html=True)
|
160 |
+
fig = create_wheel_chart(planet_positions, houses)
|
161 |
+
st.plotly_chart(fig, use_container_width=True)
|
162 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
163 |
+
|
164 |
+
st.markdown("<div class='cosmic-card'>", unsafe_allow_html=True)
|
165 |
+
st.markdown("<h3>🌍 Posições Planetárias</h3>", unsafe_allow_html=True)
|
166 |
+
for planet, data in planet_positions.items():
|
167 |
+
planet_name = PLANET_NAMES.get(planet, planet) # Usar nome traduzido
|
168 |
+
st.write(f"✨ {planet_name}: {data['longitude']:.2f}°")
|
169 |
+
|
170 |
+
st.markdown("<h3>🏠 Cúspides das Casas</h3>", unsafe_allow_html=True)
|
171 |
+
for i, cusp in enumerate(houses['cusps'], 1):
|
172 |
+
st.write(f"Casa {i}: {cusp:.2f}°")
|
173 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
174 |
+
|
175 |
+
st.markdown("<div class='cosmic-card'>", unsafe_allow_html=True)
|
176 |
+
st.markdown("<h3>🌟 Pontos Importantes</h3>", unsafe_allow_html=True)
|
177 |
+
st.write(f"⭐ Ascendente: {houses['ascendant']:.2f}°")
|
178 |
+
st.write(f"🌠 Meio do Céu: {houses['mc']:.2f}°")
|
179 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
180 |
+
else:
|
181 |
+
# Layout em duas colunas para desktop
|
182 |
+
chart_col, info_col = st.columns([2, 1])
|
183 |
+
|
184 |
+
with chart_col:
|
185 |
+
st.markdown("<div class='chart-container'>", unsafe_allow_html=True)
|
186 |
+
fig = create_wheel_chart(planet_positions, houses)
|
187 |
+
st.plotly_chart(fig, use_container_width=True)
|
188 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
189 |
+
|
190 |
+
with info_col:
|
191 |
+
st.markdown("<div class='cosmic-card'>", unsafe_allow_html=True)
|
192 |
+
st.markdown("<h3>🌍 Posições Planetárias</h3>", unsafe_allow_html=True)
|
193 |
+
for planet, data in planet_positions.items():
|
194 |
+
planet_name = PLANET_NAMES.get(planet, planet) # Usar nome traduzido
|
195 |
+
st.write(f"✨ {planet_name}: {data['longitude']:.2f}°")
|
196 |
+
|
197 |
+
st.markdown("<h3>🏠 Cúspides das Casas</h3>", unsafe_allow_html=True)
|
198 |
+
for i, cusp in enumerate(houses['cusps'], 1):
|
199 |
+
st.write(f"Casa {i}: {cusp:.2f}°")
|
200 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
201 |
+
|
202 |
+
st.markdown("<div class='cosmic-card'>", unsafe_allow_html=True)
|
203 |
+
st.markdown("<h3>🌟 Pontos Importantes</h3>", unsafe_allow_html=True)
|
204 |
+
st.write(f"⭐ Ascendente: {houses['ascendant']:.2f}°")
|
205 |
+
st.write(f"🌠 Meio do Céu: {houses['mc']:.2f}°")
|
206 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
207 |
+
|
208 |
+
except Exception as e:
|
209 |
+
st.error(f"Ocorreu um erro: {str(e)}")
|
210 |
+
|
211 |
+
# Rodapé
|
212 |
+
st.markdown("""
|
213 |
+
<div class='cosmic-card' style='text-align: center; margin-top: 2rem;'>
|
214 |
+
<p>Feito com ✨ e energia cósmica</p>
|
215 |
+
</div>
|
216 |
+
""", unsafe_allow_html=True)
|
chart_generator.py
ADDED
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import plotly.graph_objects as go
|
2 |
+
import numpy as np
|
3 |
+
|
4 |
+
THEME_COLORS = {
|
5 |
+
'dark': {
|
6 |
+
'background': 'rgba(28, 28, 28, 0.95)',
|
7 |
+
'primary': '#9C27B0',
|
8 |
+
'accent': '#FF4081',
|
9 |
+
'text': '#FFFFFF',
|
10 |
+
'grid': 'rgba(156, 39, 176, 0.2)',
|
11 |
+
'line': 'rgba(156, 39, 176, 0.5)'
|
12 |
+
},
|
13 |
+
'light': {
|
14 |
+
'background': 'rgba(255, 255, 255, 0.95)',
|
15 |
+
'primary': '#7B1FA2',
|
16 |
+
'accent': '#E91E63',
|
17 |
+
'text': '#333333',
|
18 |
+
'grid': 'rgba(123, 31, 162, 0.2)',
|
19 |
+
'line': 'rgba(123, 31, 162, 0.5)'
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
23 |
+
# Dicionário de tradução para os nomes dos planetas
|
24 |
+
PLANET_NAMES = {
|
25 |
+
'Sun': 'Sol',
|
26 |
+
'Moon': 'Lua',
|
27 |
+
'Mercury': 'Mercúrio',
|
28 |
+
'Venus': 'Vênus',
|
29 |
+
'Mars': 'Marte',
|
30 |
+
'Jupiter': 'Júpiter',
|
31 |
+
'Saturn': 'Saturno',
|
32 |
+
'Uranus': 'Urano',
|
33 |
+
'Neptune': 'Netuno',
|
34 |
+
'Pluto': 'Plutão'
|
35 |
+
}
|
36 |
+
|
37 |
+
# Dicionário de símbolos dos signos
|
38 |
+
ZODIAC_SYMBOLS = {
|
39 |
+
"Áries": "♈",
|
40 |
+
"Touro": "♉",
|
41 |
+
"Gêmeos": "♊",
|
42 |
+
"Câncer": "♋",
|
43 |
+
"Leão": "♌",
|
44 |
+
"Virgem": "♍",
|
45 |
+
"Libra": "♎",
|
46 |
+
"Escorpião": "♏",
|
47 |
+
"Sagitário": "♐",
|
48 |
+
"Capricórnio": "♑",
|
49 |
+
"Aquário": "♒",
|
50 |
+
"Peixes": "♓"
|
51 |
+
}
|
52 |
+
|
53 |
+
def calcular_signo(graus):
|
54 |
+
"""Converte uma posição em graus para o signo correspondente."""
|
55 |
+
signos = ["Áries", "Touro", "Gêmeos", "Câncer", "Leão", "Virgem",
|
56 |
+
"Libra", "Escorpião", "Sagitário", "Capricórnio", "Aquário", "Peixes"]
|
57 |
+
grau_normalizado = graus % 360
|
58 |
+
indice = int(grau_normalizado // 30)
|
59 |
+
return signos[indice]
|
60 |
+
|
61 |
+
def create_wheel_chart(planet_positions, houses, theme='dark'):
|
62 |
+
"""Gera um gráfico interativo do mapa astral usando Plotly."""
|
63 |
+
colors = THEME_COLORS[theme]
|
64 |
+
|
65 |
+
# Criar o círculo base
|
66 |
+
theta = np.linspace(0, 2*np.pi, 360)
|
67 |
+
r = np.ones_like(theta)
|
68 |
+
|
69 |
+
# Criar a figura
|
70 |
+
fig = go.Figure()
|
71 |
+
|
72 |
+
# Adicionar o círculo principal
|
73 |
+
fig.add_trace(go.Scatterpolar(
|
74 |
+
r=r,
|
75 |
+
theta=np.degrees(theta),
|
76 |
+
mode='lines',
|
77 |
+
line=dict(color=colors['primary'], width=2),
|
78 |
+
showlegend=False,
|
79 |
+
fill='toself',
|
80 |
+
fillcolor=colors['background']
|
81 |
+
))
|
82 |
+
|
83 |
+
# Adicionar linhas das casas
|
84 |
+
for house in houses['cusps']:
|
85 |
+
angle = house
|
86 |
+
fig.add_trace(go.Scatterpolar(
|
87 |
+
r=[0, 1],
|
88 |
+
theta=[angle, angle],
|
89 |
+
mode='lines',
|
90 |
+
line=dict(color=colors['line'], width=1),
|
91 |
+
showlegend=False
|
92 |
+
))
|
93 |
+
|
94 |
+
# Adicionar o signo solar com destaque
|
95 |
+
sun_longitude = planet_positions['Sun']['longitude']
|
96 |
+
sun_sign = calcular_signo(sun_longitude)
|
97 |
+
fig.add_trace(go.Scatterpolar(
|
98 |
+
r=[0.9],
|
99 |
+
theta=[sun_longitude],
|
100 |
+
mode='text',
|
101 |
+
text=[f"{sun_sign} {ZODIAC_SYMBOLS[sun_sign]}"],
|
102 |
+
textposition="middle center",
|
103 |
+
textfont=dict(
|
104 |
+
color=colors['text'],
|
105 |
+
size=24,
|
106 |
+
family="Arial"
|
107 |
+
),
|
108 |
+
showlegend=False
|
109 |
+
))
|
110 |
+
|
111 |
+
|
112 |
+
# Adicionar planetas
|
113 |
+
for planet, data in planet_positions.items():
|
114 |
+
if planet != 'Sun': #Avoid duplicating Sun
|
115 |
+
planet_name = PLANET_NAMES.get(planet, planet) # Usar nome traduzido
|
116 |
+
fig.add_trace(go.Scatterpolar(
|
117 |
+
r=[0.8],
|
118 |
+
theta=[data['longitude']],
|
119 |
+
mode='markers+text',
|
120 |
+
name=planet_name,
|
121 |
+
text=[planet_name],
|
122 |
+
textposition="top center",
|
123 |
+
textfont=dict(
|
124 |
+
color=colors['text'],
|
125 |
+
size=16, # Aumentado o tamanho da fonte
|
126 |
+
family="Arial" # Fonte mais legível
|
127 |
+
),
|
128 |
+
marker=dict(
|
129 |
+
size=18, # Aumentado o tamanho do marcador
|
130 |
+
color=colors['accent'],
|
131 |
+
symbol='star',
|
132 |
+
line=dict(
|
133 |
+
color=colors['text'],
|
134 |
+
width=1
|
135 |
+
)
|
136 |
+
),
|
137 |
+
showlegend=True
|
138 |
+
))
|
139 |
+
|
140 |
+
# Atualizar layout
|
141 |
+
fig.update_layout(
|
142 |
+
polar=dict(
|
143 |
+
radialaxis=dict(
|
144 |
+
visible=False,
|
145 |
+
range=[0, 1]
|
146 |
+
),
|
147 |
+
angularaxis=dict(
|
148 |
+
direction="clockwise",
|
149 |
+
period=360,
|
150 |
+
rotation=90,
|
151 |
+
tickmode='array',
|
152 |
+
ticktext=['♈', '♉', '♊', '♋', '♌', '♍', '♎', '♏', '♐', '♑', '♒', '♓'],
|
153 |
+
tickvals=np.arange(0, 360, 30),
|
154 |
+
tickfont=dict(
|
155 |
+
size=24, # Aumentado o tamanho dos símbolos zodiacais
|
156 |
+
color=colors['text']
|
157 |
+
),
|
158 |
+
gridcolor=colors['grid'],
|
159 |
+
linecolor=colors['line']
|
160 |
+
),
|
161 |
+
bgcolor=colors['background']
|
162 |
+
),
|
163 |
+
paper_bgcolor='rgba(0,0,0,0)',
|
164 |
+
plot_bgcolor='rgba(0,0,0,0)',
|
165 |
+
showlegend=True,
|
166 |
+
legend=dict(
|
167 |
+
bgcolor=colors['background'],
|
168 |
+
bordercolor=colors['primary'],
|
169 |
+
borderwidth=1,
|
170 |
+
font=dict(
|
171 |
+
color=colors['text'],
|
172 |
+
size=14,
|
173 |
+
family="Arial"
|
174 |
+
),
|
175 |
+
itemsizing='constant',
|
176 |
+
yanchor="top",
|
177 |
+
y=0.99,
|
178 |
+
xanchor="left",
|
179 |
+
x=1.1,
|
180 |
+
title=dict(
|
181 |
+
text="Planetas",
|
182 |
+
font=dict(
|
183 |
+
size=16,
|
184 |
+
family="Arial",
|
185 |
+
color=colors['text']
|
186 |
+
)
|
187 |
+
)
|
188 |
+
),
|
189 |
+
margin=dict(l=20, r=120, t=20, b=20), # Ajustado para acomodar a legenda
|
190 |
+
height=800
|
191 |
+
)
|
192 |
+
|
193 |
+
return fig
|
custom.css
ADDED
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Custom styling for the mystical theme */
|
2 |
+
.stApp {
|
3 |
+
background: linear-gradient(135deg, #1a1a1a 0%, #2d1a2f 100%);
|
4 |
+
font-family: 'Poppins', sans-serif;
|
5 |
+
letter-spacing: 0.02em;
|
6 |
+
line-height: 1.6;
|
7 |
+
}
|
8 |
+
|
9 |
+
/* Card styling */
|
10 |
+
.cosmic-card {
|
11 |
+
background: rgba(28, 28, 28, 0.95);
|
12 |
+
border: 1px solid rgba(156, 39, 176, 0.3);
|
13 |
+
border-radius: 15px;
|
14 |
+
padding: 25px;
|
15 |
+
box-shadow: 0 0 20px rgba(156, 39, 176, 0.2);
|
16 |
+
transition: all 0.3s ease;
|
17 |
+
margin-bottom: 20px;
|
18 |
+
}
|
19 |
+
|
20 |
+
.cosmic-card:hover {
|
21 |
+
box-shadow: 0 0 30px rgba(156, 39, 176, 0.4);
|
22 |
+
transform: translateY(-2px);
|
23 |
+
}
|
24 |
+
|
25 |
+
/* Text styling */
|
26 |
+
.glow-text {
|
27 |
+
color: #FF4081;
|
28 |
+
text-shadow: 0 0 10px rgba(255, 64, 129, 0.5);
|
29 |
+
font-size: 2.5rem;
|
30 |
+
font-weight: 600;
|
31 |
+
text-align: center;
|
32 |
+
margin-bottom: 30px;
|
33 |
+
}
|
34 |
+
|
35 |
+
h1, h2, h3 {
|
36 |
+
color: #FFFFFF;
|
37 |
+
margin-bottom: 20px;
|
38 |
+
font-weight: 600;
|
39 |
+
}
|
40 |
+
|
41 |
+
p, li {
|
42 |
+
color: #E0E0E0;
|
43 |
+
font-size: 1.1rem;
|
44 |
+
line-height: 1.8;
|
45 |
+
}
|
46 |
+
|
47 |
+
/* Chart container styling */
|
48 |
+
.chart-container {
|
49 |
+
border-radius: 50%;
|
50 |
+
box-shadow: 0 0 30px rgba(156, 39, 176, 0.3);
|
51 |
+
padding: 20px;
|
52 |
+
background: rgba(28, 28, 28, 0.8);
|
53 |
+
transition: all 0.3s ease;
|
54 |
+
}
|
55 |
+
|
56 |
+
.chart-container:hover {
|
57 |
+
box-shadow: 0 0 40px rgba(156, 39, 176, 0.5);
|
58 |
+
}
|
59 |
+
|
60 |
+
/* Form inputs styling */
|
61 |
+
.stTextInput > div > div, .stDateInput > div > div, .stTimeInput > div > div {
|
62 |
+
background: rgba(40, 40, 40, 0.9) !important;
|
63 |
+
border: 1px solid rgba(156, 39, 176, 0.5) !important;
|
64 |
+
border-radius: 8px !important;
|
65 |
+
color: #FFFFFF !important;
|
66 |
+
font-size: 1.1rem !important;
|
67 |
+
transition: all 0.3s ease;
|
68 |
+
}
|
69 |
+
|
70 |
+
.stTextInput > div > div:focus-within,
|
71 |
+
.stDateInput > div > div:focus-within,
|
72 |
+
.stTimeInput > div > div:focus-within {
|
73 |
+
border-color: #FF4081 !important;
|
74 |
+
box-shadow: 0 0 10px rgba(255, 64, 129, 0.3) !important;
|
75 |
+
}
|
76 |
+
|
77 |
+
/* Button styling */
|
78 |
+
.stButton > button {
|
79 |
+
background: linear-gradient(135deg, #9C27B0 0%, #FF4081 100%) !important;
|
80 |
+
color: white !important;
|
81 |
+
border: none !important;
|
82 |
+
padding: 12px 30px !important;
|
83 |
+
border-radius: 8px !important;
|
84 |
+
font-size: 1.1rem !important;
|
85 |
+
font-weight: 600 !important;
|
86 |
+
letter-spacing: 0.05em !important;
|
87 |
+
transition: all 0.3s ease !important;
|
88 |
+
text-transform: uppercase;
|
89 |
+
}
|
90 |
+
|
91 |
+
.stButton > button:hover {
|
92 |
+
transform: translateY(-2px) !important;
|
93 |
+
box-shadow: 0 5px 15px rgba(156, 39, 176, 0.4) !important;
|
94 |
+
}
|
95 |
+
|
96 |
+
.stButton > button:active {
|
97 |
+
transform: translateY(1px) !important;
|
98 |
+
}
|
99 |
+
|
100 |
+
/* Labels and text inputs */
|
101 |
+
.stTextInput label, .stDateInput label, .stTimeInput label {
|
102 |
+
color: #FFFFFF !important;
|
103 |
+
font-size: 1.1rem !important;
|
104 |
+
font-weight: 500 !important;
|
105 |
+
margin-bottom: 8px !important;
|
106 |
+
}
|
107 |
+
|
108 |
+
/* Loading spinner */
|
109 |
+
.stSpinner > div {
|
110 |
+
border-color: #FF4081 !important;
|
111 |
+
}
|
112 |
+
|
113 |
+
/* Error messages */
|
114 |
+
.stAlert {
|
115 |
+
background: rgba(244, 67, 54, 0.1) !important;
|
116 |
+
border-color: rgba(244, 67, 54, 0.5) !important;
|
117 |
+
color: #FFFFFF !important;
|
118 |
+
}
|
119 |
+
|
120 |
+
/* Success messages */
|
121 |
+
.stSuccess {
|
122 |
+
background: rgba(76, 175, 80, 0.1) !important;
|
123 |
+
border-color: rgba(76, 175, 80, 0.5) !important;
|
124 |
+
color: #FFFFFF !important;
|
125 |
+
}
|
126 |
+
|
127 |
+
/* Estilo especial para o card do signo solar */
|
128 |
+
.signo-solar-card {
|
129 |
+
background: linear-gradient(135deg, rgba(156, 39, 176, 0.2) 0%, rgba(255, 64, 129, 0.2) 100%);
|
130 |
+
text-align: center;
|
131 |
+
padding: 2rem;
|
132 |
+
margin-bottom: 2rem;
|
133 |
+
border: 2px solid rgba(156, 39, 176, 0.5);
|
134 |
+
transform: translateY(0);
|
135 |
+
transition: all 0.3s ease;
|
136 |
+
}
|
137 |
+
|
138 |
+
.signo-solar-card:hover {
|
139 |
+
transform: translateY(-5px);
|
140 |
+
box-shadow: 0 10px 30px rgba(156, 39, 176, 0.3);
|
141 |
+
}
|
142 |
+
|
143 |
+
.signo-symbol {
|
144 |
+
font-size: 4rem;
|
145 |
+
color: #FF4081;
|
146 |
+
text-shadow: 0 0 20px rgba(255, 64, 129, 0.5);
|
147 |
+
margin-bottom: 1rem;
|
148 |
+
animation: float 3s ease-in-out infinite;
|
149 |
+
}
|
150 |
+
|
151 |
+
.signo-nome {
|
152 |
+
font-size: 2.5rem;
|
153 |
+
color: #FFFFFF;
|
154 |
+
margin: 1rem 0;
|
155 |
+
font-weight: 600;
|
156 |
+
letter-spacing: 0.1em;
|
157 |
+
}
|
158 |
+
|
159 |
+
.signo-grau {
|
160 |
+
font-size: 1.2rem;
|
161 |
+
color: #E0E0E0;
|
162 |
+
font-family: 'Courier New', monospace;
|
163 |
+
}
|
164 |
+
|
165 |
+
@keyframes float {
|
166 |
+
0% {
|
167 |
+
transform: translateY(0px);
|
168 |
+
}
|
169 |
+
50% {
|
170 |
+
transform: translateY(-10px);
|
171 |
+
}
|
172 |
+
100% {
|
173 |
+
transform: translateY(0px);
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
/* Media Queries para Responsividade */
|
178 |
+
@media screen and (max-width: 768px) {
|
179 |
+
.glow-text {
|
180 |
+
font-size: 1.8rem;
|
181 |
+
}
|
182 |
+
|
183 |
+
.cosmic-card {
|
184 |
+
padding: 15px;
|
185 |
+
margin-bottom: 15px;
|
186 |
+
}
|
187 |
+
|
188 |
+
.signo-symbol {
|
189 |
+
font-size: 3rem;
|
190 |
+
}
|
191 |
+
|
192 |
+
.signo-nome {
|
193 |
+
font-size: 2rem;
|
194 |
+
}
|
195 |
+
|
196 |
+
.chart-container {
|
197 |
+
padding: 10px;
|
198 |
+
}
|
199 |
+
|
200 |
+
.stButton > button {
|
201 |
+
padding: 10px 20px !important;
|
202 |
+
font-size: 1rem !important;
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
@media screen and (max-width: 480px) {
|
207 |
+
.glow-text {
|
208 |
+
font-size: 1.5rem;
|
209 |
+
}
|
210 |
+
|
211 |
+
.cosmic-card {
|
212 |
+
padding: 10px;
|
213 |
+
margin-bottom: 10px;
|
214 |
+
}
|
215 |
+
|
216 |
+
p, li {
|
217 |
+
font-size: 1rem;
|
218 |
+
}
|
219 |
+
|
220 |
+
.signo-symbol {
|
221 |
+
font-size: 2.5rem;
|
222 |
+
}
|
223 |
+
|
224 |
+
.signo-nome {
|
225 |
+
font-size: 1.8rem;
|
226 |
+
}
|
227 |
+
|
228 |
+
.signo-grau {
|
229 |
+
font-size: 1rem;
|
230 |
+
}
|
231 |
+
|
232 |
+
.stTextInput > div > div,
|
233 |
+
.stDateInput > div > div,
|
234 |
+
.stTimeInput > div > div {
|
235 |
+
font-size: 1rem !important;
|
236 |
+
}
|
237 |
+
}
|
generated-icon.png
ADDED
![]() |
Git LFS Details
|
pyproject.toml
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[project]
|
2 |
+
name = "repl-nix-workspace"
|
3 |
+
version = "0.1.0"
|
4 |
+
description = "Add your description here"
|
5 |
+
requires-python = ">=3.11"
|
6 |
+
dependencies = [
|
7 |
+
"geopy>=2.4.1",
|
8 |
+
"numpy>=2.2.3",
|
9 |
+
"plotly>=6.0.0",
|
10 |
+
"pyswisseph>=2.10.3.2",
|
11 |
+
"pytz>=2025.1",
|
12 |
+
"requests>=2.32.3",
|
13 |
+
"streamlit>=1.42.2",
|
14 |
+
"swisseph>=0.0.0.dev1",
|
15 |
+
"timezonefinder>=6.5.8",
|
16 |
+
"trafilatura>=2.0.0",
|
17 |
+
]
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit==1.42.2
|
2 |
+
geopy
|
3 |
+
numpy
|
4 |
+
plotly
|
5 |
+
pyswisseph
|
6 |
+
pytz
|
7 |
+
requests
|
8 |
+
swisseph
|
9 |
+
timezonefinder
|
seas_18.se1
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5fd9c2aa1654e37c09a6aeb558076e795409b7dc4bd948ebc0faa7d4a7686b5b
|
3 |
+
size 223002
|
semo_18.se1
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ecfa54dbf5bc0b5a9bc3e04ed28629a821e98625eacae38f4070593bba0e2980
|
3 |
+
size 1304771
|
sepl_18.se1
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:0b7e416e3c1be9e6a0dd1d711dae7f7685793a0e7df13f76363a493dc27b6ea1
|
3 |
+
size 484055
|
streamlit.service
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[Unit]
|
2 |
+
Description=Streamlit Mapa Astral
|
3 |
+
After=network.target
|
4 |
+
|
5 |
+
[Service]
|
6 |
+
User=root
|
7 |
+
WorkingDirectory=/var/www/mapa-astral
|
8 |
+
Environment="PATH=/var/www/mapa-astral/venv/bin"
|
9 |
+
ExecStart=/var/www/mapa-astral/venv/bin/streamlit run app.py --server.port=8501 --server.address=0.0.0.0
|
10 |
+
|
11 |
+
[Install]
|
12 |
+
WantedBy=multi-user.target
|
utils.py
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import swisseph as swe
|
2 |
+
from datetime import datetime
|
3 |
+
import pytz
|
4 |
+
from geopy.geocoders import Nominatim
|
5 |
+
from timezonefinder import TimezoneFinder
|
6 |
+
import requests
|
7 |
+
from pathlib import Path
|
8 |
+
|
9 |
+
def download_ephe_files():
|
10 |
+
"""Download and configure ephemeris files."""
|
11 |
+
ephe_dir = Path("ephe")
|
12 |
+
ephe_dir.mkdir(exist_ok=True)
|
13 |
+
|
14 |
+
base_url = "https://raw.githubusercontent.com/aloistr/swisseph/master/ephe/"
|
15 |
+
essential_files = ["seas_18.se1", "semo_18.se1", "sepl_18.se1"]
|
16 |
+
|
17 |
+
for filename in essential_files:
|
18 |
+
file_path = ephe_dir / filename
|
19 |
+
if not file_path.exists():
|
20 |
+
try:
|
21 |
+
response = requests.get(f"{base_url}{filename}")
|
22 |
+
response.raise_for_status()
|
23 |
+
with open(file_path, "wb") as f:
|
24 |
+
f.write(response.content)
|
25 |
+
except Exception:
|
26 |
+
return False
|
27 |
+
|
28 |
+
swe.set_ephe_path(str(ephe_dir))
|
29 |
+
return True
|
30 |
+
|
31 |
+
def get_location_data(location_string):
|
32 |
+
"""Get coordinates and timezone for a location."""
|
33 |
+
geolocator = Nominatim(user_agent="mystical_chart")
|
34 |
+
location = geolocator.geocode(location_string)
|
35 |
+
if not location:
|
36 |
+
raise ValueError("Location not found")
|
37 |
+
|
38 |
+
tf = TimezoneFinder()
|
39 |
+
timezone_str = tf.timezone_at(lat=location.latitude, lng=location.longitude)
|
40 |
+
|
41 |
+
return {
|
42 |
+
'latitude': location.latitude,
|
43 |
+
'longitude': location.longitude,
|
44 |
+
'timezone': timezone_str
|
45 |
+
}
|
46 |
+
|
47 |
+
def calculate_julian_day(date, time, timezone_str):
|
48 |
+
"""Calculate Julian Day from date and time."""
|
49 |
+
tz = pytz.timezone(timezone_str)
|
50 |
+
datetime_obj = datetime.combine(date, time)
|
51 |
+
local_dt = tz.localize(datetime_obj)
|
52 |
+
utc_dt = local_dt.astimezone(pytz.UTC)
|
53 |
+
|
54 |
+
jd = swe.utc_to_jd(
|
55 |
+
utc_dt.year, utc_dt.month, utc_dt.day,
|
56 |
+
utc_dt.hour, utc_dt.minute, utc_dt.second,
|
57 |
+
swe.GREG_CAL
|
58 |
+
)[1]
|
59 |
+
|
60 |
+
return jd
|
61 |
+
|
62 |
+
def get_planet_positions(jd):
|
63 |
+
"""Calculate positions for all planets."""
|
64 |
+
planets = {
|
65 |
+
"Sun": swe.SUN,
|
66 |
+
"Moon": swe.MOON,
|
67 |
+
"Mercury": swe.MERCURY,
|
68 |
+
"Venus": swe.VENUS,
|
69 |
+
"Mars": swe.MARS,
|
70 |
+
"Jupiter": swe.JUPITER,
|
71 |
+
"Saturn": swe.SATURN,
|
72 |
+
"Uranus": swe.URANUS,
|
73 |
+
"Neptune": swe.NEPTUNE,
|
74 |
+
"Pluto": swe.PLUTO
|
75 |
+
}
|
76 |
+
|
77 |
+
positions = {}
|
78 |
+
for name, planet_id in planets.items():
|
79 |
+
result = swe.calc_ut(jd, planet_id)
|
80 |
+
positions[name] = {
|
81 |
+
'longitude': result[0][0],
|
82 |
+
'latitude': result[0][1],
|
83 |
+
'distance': result[0][2]
|
84 |
+
}
|
85 |
+
|
86 |
+
return positions
|
87 |
+
|
88 |
+
def calculate_houses(jd, lat, lon):
|
89 |
+
"""Calculate house cusps using Placidus system."""
|
90 |
+
houses, angles = swe.houses(jd, lat, lon, b'P')
|
91 |
+
return {
|
92 |
+
'cusps': houses,
|
93 |
+
'ascendant': angles[0],
|
94 |
+
'mc': angles[1],
|
95 |
+
'armc': angles[2],
|
96 |
+
'vertex': angles[3]
|
97 |
+
}
|
uv.lock
ADDED
The diff for this file is too large to render.
See raw diff
|
|