solarmoonpy 1.0.5__py3-none-any.whl → 1.0.7__py3-none-any.whl
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.
Potentially problematic release.
This version of solarmoonpy might be problematic. Click here for more details.
- solarmoonpy/location.py +156 -7
- solarmoonpy/sun.py +112 -1
- solarmoonpy/version.py +1 -1
- {solarmoonpy-1.0.5.dist-info → solarmoonpy-1.0.7.dist-info}/METADATA +1 -1
- solarmoonpy-1.0.7.dist-info/RECORD +10 -0
- solarmoonpy-1.0.5.dist-info/RECORD +0 -10
- {solarmoonpy-1.0.5.dist-info → solarmoonpy-1.0.7.dist-info}/WHEEL +0 -0
- {solarmoonpy-1.0.5.dist-info → solarmoonpy-1.0.7.dist-info}/licenses/LICENSE +0 -0
- {solarmoonpy-1.0.5.dist-info → solarmoonpy-1.0.7.dist-info}/top_level.txt +0 -0
solarmoonpy/location.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from datetime import date, datetime, timezone
|
|
3
3
|
from zoneinfo import ZoneInfo
|
|
4
|
-
from typing import Optional, Union
|
|
4
|
+
from typing import Optional, Union, Literal, Dict
|
|
5
5
|
|
|
6
|
-
from .sun import sunrise_sunset, noon # Importar funciones de sun.py
|
|
6
|
+
from .sun import sunrise_sunset, noon, dawn, dusk, midnight # Importar funciones de sun.py
|
|
7
7
|
|
|
8
8
|
@dataclass
|
|
9
9
|
class LocationInfo:
|
|
@@ -257,15 +257,80 @@ class Location:
|
|
|
257
257
|
date=date,
|
|
258
258
|
timezone=self.tzinfo if local else timezone.utc
|
|
259
259
|
)
|
|
260
|
+
|
|
261
|
+
def dawn(
|
|
262
|
+
self,
|
|
263
|
+
date: Optional[date] = None,
|
|
264
|
+
local: bool = True,
|
|
265
|
+
twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
|
|
266
|
+
elevation: Optional[float] = None
|
|
267
|
+
) -> Optional[datetime]:
|
|
268
|
+
"""🌅 Calcula amanecer (inicio crepúsculo)."""
|
|
269
|
+
if local and self.timezone is None:
|
|
270
|
+
raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
|
|
260
271
|
|
|
261
|
-
|
|
272
|
+
date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
|
|
273
|
+
elevation = elevation if elevation is not None else self.elevation
|
|
274
|
+
|
|
275
|
+
return dawn(
|
|
276
|
+
latitude=self.latitude,
|
|
277
|
+
longitude=self.longitude,
|
|
278
|
+
date=date,
|
|
279
|
+
twilight_type=twilight_type,
|
|
280
|
+
elevation=elevation,
|
|
281
|
+
timezone=self.tzinfo if local else timezone.utc,
|
|
282
|
+
with_refraction=True
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
def dusk(
|
|
262
286
|
self,
|
|
263
287
|
date: Optional[date] = None,
|
|
264
288
|
local: bool = True,
|
|
289
|
+
twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
|
|
265
290
|
elevation: Optional[float] = None
|
|
266
|
-
) ->
|
|
291
|
+
) -> Optional[datetime]:
|
|
292
|
+
"""🌙 Calcula anochecer (fin crepúsculo)."""
|
|
293
|
+
if local and self.timezone is None:
|
|
294
|
+
raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
|
|
295
|
+
|
|
296
|
+
date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
|
|
297
|
+
elevation = elevation if elevation is not None else self.elevation
|
|
298
|
+
|
|
299
|
+
return dusk(
|
|
300
|
+
latitude=self.latitude,
|
|
301
|
+
longitude=self.longitude,
|
|
302
|
+
date=date,
|
|
303
|
+
twilight_type=twilight_type,
|
|
304
|
+
elevation=elevation,
|
|
305
|
+
timezone=self.tzinfo if local else timezone.utc,
|
|
306
|
+
with_refraction=True
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def midnight(
|
|
310
|
+
self,
|
|
311
|
+
date: Optional[date] = None,
|
|
312
|
+
local: bool = True
|
|
313
|
+
) -> datetime:
|
|
314
|
+
"""🌑 Calcula medianoche solar."""
|
|
315
|
+
if local and self.timezone is None:
|
|
316
|
+
raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
|
|
317
|
+
|
|
318
|
+
date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
|
|
319
|
+
|
|
320
|
+
return midnight(
|
|
321
|
+
longitude=self.longitude,
|
|
322
|
+
date=date,
|
|
323
|
+
timezone=self.tzinfo if local else timezone.utc
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
def sun_events(
|
|
327
|
+
self,
|
|
328
|
+
date: Optional[date] = None,
|
|
329
|
+
local: bool = True,
|
|
330
|
+
elevation: Optional[float] = None,
|
|
331
|
+
) -> Dict:
|
|
267
332
|
"""
|
|
268
|
-
Devuelve un diccionario con
|
|
333
|
+
Devuelve un diccionario con los eventos solares para una fecha dada, incluyendo todos los tipos de crepúsculo.
|
|
269
334
|
|
|
270
335
|
Args:
|
|
271
336
|
date: Fecha para la cual calcular los eventos solares. Si es None, usa la fecha actual.
|
|
@@ -274,9 +339,17 @@ class Location:
|
|
|
274
339
|
|
|
275
340
|
Returns:
|
|
276
341
|
dict: {
|
|
342
|
+
"dawn_civil": datetime | None,
|
|
343
|
+
"dawn_nautical": datetime | None,
|
|
344
|
+
"dawn_astronomical": datetime | None,
|
|
277
345
|
"sunrise": datetime,
|
|
278
346
|
"noon": datetime,
|
|
279
|
-
"sunset": datetime
|
|
347
|
+
"sunset": datetime,
|
|
348
|
+
"dusk_civil": datetime | None,
|
|
349
|
+
"dusk_nautical": datetime | None,
|
|
350
|
+
"dusk_astronomical": datetime | None,
|
|
351
|
+
"midnight": datetime,
|
|
352
|
+
"daylight_duration": float | None
|
|
280
353
|
}
|
|
281
354
|
"""
|
|
282
355
|
if local and self.timezone is None:
|
|
@@ -303,8 +376,84 @@ class Location:
|
|
|
303
376
|
timezone=tz,
|
|
304
377
|
)
|
|
305
378
|
|
|
379
|
+
# Calcular medianoche solar
|
|
380
|
+
midnight_time = midnight(
|
|
381
|
+
longitude=self.longitude,
|
|
382
|
+
date=date,
|
|
383
|
+
timezone=tz,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Calcular duración del día (en horas)
|
|
387
|
+
daylight_duration = None
|
|
388
|
+
if sunrise and sunset:
|
|
389
|
+
daylight_duration = (sunset - sunrise).total_seconds() / 3600
|
|
390
|
+
|
|
391
|
+
# Calcular dawn y dusk para cada tipo de crepúsculo
|
|
392
|
+
dawn_civil = dawn(
|
|
393
|
+
latitude=self.latitude,
|
|
394
|
+
longitude=self.longitude,
|
|
395
|
+
date=date,
|
|
396
|
+
twilight_type="civil",
|
|
397
|
+
elevation=elevation,
|
|
398
|
+
timezone=tz,
|
|
399
|
+
with_refraction=True,
|
|
400
|
+
)
|
|
401
|
+
dusk_civil = dusk(
|
|
402
|
+
latitude=self.latitude,
|
|
403
|
+
longitude=self.longitude,
|
|
404
|
+
date=date,
|
|
405
|
+
twilight_type="civil",
|
|
406
|
+
elevation=elevation,
|
|
407
|
+
timezone=tz,
|
|
408
|
+
with_refraction=True,
|
|
409
|
+
)
|
|
410
|
+
dawn_nautical = dawn(
|
|
411
|
+
latitude=self.latitude,
|
|
412
|
+
longitude=self.longitude,
|
|
413
|
+
date=date,
|
|
414
|
+
twilight_type="nautical",
|
|
415
|
+
elevation=elevation,
|
|
416
|
+
timezone=tz,
|
|
417
|
+
with_refraction=True,
|
|
418
|
+
)
|
|
419
|
+
dusk_nautical = dusk(
|
|
420
|
+
latitude=self.latitude,
|
|
421
|
+
longitude=self.longitude,
|
|
422
|
+
date=date,
|
|
423
|
+
twilight_type="nautical",
|
|
424
|
+
elevation=elevation,
|
|
425
|
+
timezone=tz,
|
|
426
|
+
with_refraction=True,
|
|
427
|
+
)
|
|
428
|
+
dawn_astronomical = dawn(
|
|
429
|
+
latitude=self.latitude,
|
|
430
|
+
longitude=self.longitude,
|
|
431
|
+
date=date,
|
|
432
|
+
twilight_type="astronomical",
|
|
433
|
+
elevation=elevation,
|
|
434
|
+
timezone=tz,
|
|
435
|
+
with_refraction=True,
|
|
436
|
+
)
|
|
437
|
+
dusk_astronomical = dusk(
|
|
438
|
+
latitude=self.latitude,
|
|
439
|
+
longitude=self.longitude,
|
|
440
|
+
date=date,
|
|
441
|
+
twilight_type="astronomical",
|
|
442
|
+
elevation=elevation,
|
|
443
|
+
timezone=tz,
|
|
444
|
+
with_refraction=True,
|
|
445
|
+
)
|
|
446
|
+
|
|
306
447
|
return {
|
|
448
|
+
"dawn_civil": dawn_civil,
|
|
449
|
+
"dawn_nautical": dawn_nautical,
|
|
450
|
+
"dawn_astronomical": dawn_astronomical,
|
|
307
451
|
"sunrise": sunrise,
|
|
308
452
|
"noon": noon_time,
|
|
309
453
|
"sunset": sunset,
|
|
310
|
-
|
|
454
|
+
"dusk_civil": dusk_civil,
|
|
455
|
+
"dusk_nautical": dusk_nautical,
|
|
456
|
+
"dusk_astronomical": dusk_astronomical,
|
|
457
|
+
"midnight": midnight_time,
|
|
458
|
+
"daylight_duration": daylight_duration,
|
|
459
|
+
}
|
solarmoonpy/sun.py
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
from math import acos, asin, atan2, cos, degrees, radians, sin, tan, sqrt
|
|
3
|
+
from typing import Optional, Tuple, Literal
|
|
4
|
+
|
|
5
|
+
# Constantes estándar para diferentes eventos solares
|
|
6
|
+
SUN_APPARENT_RADIUS = 959.63 / 3600.0 # ≈ 0.2666° (radio aparente del Sol)
|
|
7
|
+
|
|
8
|
+
# Zenith angles estándar (90° = horizonte)
|
|
9
|
+
ZENITH_SUNRISE_SUNSET = 90.0 + SUN_APPARENT_RADIUS
|
|
10
|
+
ZENITH_CIVIL_DAWN_DUSK = 96.0
|
|
11
|
+
ZENITH_NAUTICAL_DAWN_DUSK = 102.0
|
|
12
|
+
ZENITH_ASTRONOMICAL_DAWN_DUSK = 108.0
|
|
3
13
|
|
|
4
14
|
def julianday(date_or_dt: datetime.date | datetime.datetime) -> float:
|
|
5
15
|
"""
|
|
@@ -239,4 +249,105 @@ def noon(
|
|
|
239
249
|
time_utc = (720.0 - (4 * longitude) - eqtime) # minutos
|
|
240
250
|
noon_utc = datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc) \
|
|
241
251
|
+ datetime.timedelta(minutes=time_utc)
|
|
242
|
-
return noon_utc.astimezone(timezone)
|
|
252
|
+
return noon_utc.astimezone(timezone)
|
|
253
|
+
|
|
254
|
+
def dawn(
|
|
255
|
+
latitude: float, longitude: float, date: datetime.date,
|
|
256
|
+
twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
|
|
257
|
+
elevation: float = 0.0,
|
|
258
|
+
timezone: datetime.timezone = datetime.timezone.utc,
|
|
259
|
+
with_refraction: bool = True
|
|
260
|
+
) -> Optional[datetime.datetime]:
|
|
261
|
+
"""🌅 Calcula amanecer (inicio crepúsculo)."""
|
|
262
|
+
zeniths = {"civil": 96.0, "nautical": 102.0, "astronomical": 108.0}
|
|
263
|
+
zenith = zeniths[twilight_type]
|
|
264
|
+
|
|
265
|
+
# Reutiliza la misma lógica que sunrise_sunset pero para un zenith específico
|
|
266
|
+
adjustment_for_elevation = adjust_to_horizon(elevation)
|
|
267
|
+
adjusted_zenith = zenith + adjustment_for_elevation
|
|
268
|
+
if with_refraction:
|
|
269
|
+
adjusted_zenith += refraction_at_zenith(adjusted_zenith)
|
|
270
|
+
|
|
271
|
+
latitude = max(min(latitude, 89.8), -89.8)
|
|
272
|
+
jd = julianday(date)
|
|
273
|
+
jc = julianday_to_juliancentury(jd)
|
|
274
|
+
declination = sun_declination(jc)
|
|
275
|
+
eqtime = eq_of_time(jc)
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
ha_rising = hour_angle(latitude, declination, adjusted_zenith, "rising")
|
|
279
|
+
except ValueError:
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
delta = -longitude - degrees(ha_rising)
|
|
283
|
+
time_utc_minutes = 720.0 + (delta * 4.0) - eqtime
|
|
284
|
+
if time_utc_minutes < -720.0:
|
|
285
|
+
time_utc_minutes += 1440
|
|
286
|
+
|
|
287
|
+
base_dt = datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc)
|
|
288
|
+
result_utc = base_dt + datetime.timedelta(minutes=time_utc_minutes)
|
|
289
|
+
return result_utc.astimezone(timezone)
|
|
290
|
+
|
|
291
|
+
def dusk(
|
|
292
|
+
latitude: float, longitude: float, date: datetime.date,
|
|
293
|
+
twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
|
|
294
|
+
elevation: float = 0.0,
|
|
295
|
+
timezone: datetime.timezone = datetime.timezone.utc,
|
|
296
|
+
with_refraction: bool = True
|
|
297
|
+
) -> Optional[datetime.datetime]:
|
|
298
|
+
"""🌙 Calcula anochecer (fin crepúsculo)."""
|
|
299
|
+
zeniths = {"civil": 96.0, "nautical": 102.0, "astronomical": 108.0}
|
|
300
|
+
zenith = zeniths[twilight_type]
|
|
301
|
+
|
|
302
|
+
adjustment_for_elevation = adjust_to_horizon(elevation)
|
|
303
|
+
adjusted_zenith = zenith + adjustment_for_elevation
|
|
304
|
+
if with_refraction:
|
|
305
|
+
adjusted_zenith += refraction_at_zenith(adjusted_zenith)
|
|
306
|
+
|
|
307
|
+
latitude = max(min(latitude, 89.8), -89.8)
|
|
308
|
+
jd = julianday(date)
|
|
309
|
+
jc = julianday_to_juliancentury(jd)
|
|
310
|
+
declination = sun_declination(jc)
|
|
311
|
+
eqtime = eq_of_time(jc)
|
|
312
|
+
|
|
313
|
+
try:
|
|
314
|
+
ha_setting = hour_angle(latitude, declination, adjusted_zenith, "setting")
|
|
315
|
+
except ValueError:
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
delta = -longitude - degrees(ha_setting)
|
|
319
|
+
time_utc_minutes = 720.0 + (delta * 4.0) - eqtime
|
|
320
|
+
if time_utc_minutes < -720.0:
|
|
321
|
+
time_utc_minutes += 1440
|
|
322
|
+
|
|
323
|
+
base_dt = datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc)
|
|
324
|
+
result_utc = base_dt + datetime.timedelta(minutes=time_utc_minutes)
|
|
325
|
+
return result_utc.astimezone(timezone)
|
|
326
|
+
|
|
327
|
+
def midnight(
|
|
328
|
+
longitude: float,
|
|
329
|
+
date: datetime.date,
|
|
330
|
+
timezone: datetime.timezone = datetime.timezone.utc
|
|
331
|
+
) -> datetime.datetime:
|
|
332
|
+
"""🌑 Medianoche solar (12h después del mediodía solar)."""
|
|
333
|
+
noon_time = noon(longitude, date, timezone)
|
|
334
|
+
return noon_time + datetime.timedelta(hours=12)
|
|
335
|
+
|
|
336
|
+
# 🎁 FUNCIÓN BONUS: Todo junto (opcional)
|
|
337
|
+
def all_sun_events(
|
|
338
|
+
latitude: float, longitude: float, date: datetime.date,
|
|
339
|
+
twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
|
|
340
|
+
elevation: float = 0.0,
|
|
341
|
+
timezone: datetime.timezone = datetime.timezone.utc,
|
|
342
|
+
with_refraction: bool = True
|
|
343
|
+
) -> dict:
|
|
344
|
+
"""🔥 Calcula TODOS los eventos solares del día."""
|
|
345
|
+
sr, ss = sunrise_sunset(latitude, longitude, date, elevation, timezone, with_refraction)
|
|
346
|
+
return {
|
|
347
|
+
"dawn": dawn(latitude, longitude, date, twilight_type, elevation, timezone, with_refraction),
|
|
348
|
+
"sunrise": sr,
|
|
349
|
+
"noon": noon(longitude, date, timezone),
|
|
350
|
+
"sunset": ss,
|
|
351
|
+
"dusk": dusk(latitude, longitude, date, twilight_type, elevation, timezone, with_refraction),
|
|
352
|
+
"midnight": midnight(longitude, date, timezone)
|
|
353
|
+
}
|
solarmoonpy/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# version.py
|
|
2
|
-
__version__ = "1.0.
|
|
2
|
+
__version__ = "1.0.7"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
solarmoonpy/__init__.py,sha256=cS0ZjePNX75usK8mgvbk_uguVpXRixqZU4zjrEtg42c,722
|
|
2
|
+
solarmoonpy/location.py,sha256=vO39N1IWWchztZAkPh1QAG65bVa06vWYKjUoGOchxtY,15672
|
|
3
|
+
solarmoonpy/moon.py,sha256=yhJSsED7AWK3nF79fjqFlCeqYjiXXFhqGE36Nyfp0qk,19699
|
|
4
|
+
solarmoonpy/sun.py,sha256=K31_btucloC5WYnltFb_X_3hF1Xk0_5aQ1hRSrYP7C8,13396
|
|
5
|
+
solarmoonpy/version.py,sha256=1WAI3HCLSBsNOYFDrx3iybB2BK0q9VjsVmNPgtn8gxw,35
|
|
6
|
+
solarmoonpy-1.0.7.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
7
|
+
solarmoonpy-1.0.7.dist-info/METADATA,sha256=2-EtubwWPbvTXa3Fpz5daf9FxHqIAHNd8aaPa_hTzDI,15023
|
|
8
|
+
solarmoonpy-1.0.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
solarmoonpy-1.0.7.dist-info/top_level.txt,sha256=egsoDe9E0QEu5GfAA5jSvy_3qTCWMLMe_Kh3gnM6-dY,12
|
|
10
|
+
solarmoonpy-1.0.7.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
solarmoonpy/__init__.py,sha256=cS0ZjePNX75usK8mgvbk_uguVpXRixqZU4zjrEtg42c,722
|
|
2
|
-
solarmoonpy/location.py,sha256=CcIAlghZ6QQKiQXygfaAs4aBN_7ANfoszabZycIPY30,10312
|
|
3
|
-
solarmoonpy/moon.py,sha256=yhJSsED7AWK3nF79fjqFlCeqYjiXXFhqGE36Nyfp0qk,19699
|
|
4
|
-
solarmoonpy/sun.py,sha256=dleHQWXzjnqtPAZr9ONdXiZvTVWvdW6gW775A2ASsNk,8937
|
|
5
|
-
solarmoonpy/version.py,sha256=l3Zi-Xuhc76_pomaTnxWfoGjGfeI-KyhHlKy8KJdeOA,35
|
|
6
|
-
solarmoonpy-1.0.5.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
7
|
-
solarmoonpy-1.0.5.dist-info/METADATA,sha256=aXlHhlDVG5xxhH0DwMP-1oojRDsjtZgI2he9rIOA9xM,15023
|
|
8
|
-
solarmoonpy-1.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
-
solarmoonpy-1.0.5.dist-info/top_level.txt,sha256=egsoDe9E0QEu5GfAA5jSvy_3qTCWMLMe_Kh3gnM6-dY,12
|
|
10
|
-
solarmoonpy-1.0.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|