solarmoonpy 1.0.5__py3-none-any.whl → 1.0.6__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.
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
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:
@@ -308,3 +308,68 @@ class Location:
308
308
  "noon": noon_time,
309
309
  "sunset": sunset,
310
310
  }
311
+
312
+ def dawn(
313
+ self,
314
+ date: Optional[date] = None,
315
+ local: bool = True,
316
+ twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
317
+ elevation: Optional[float] = None
318
+ ) -> Optional[datetime]:
319
+ """🌅 Calcula amanecer (inicio crepúsculo)."""
320
+ if local and self.timezone is None:
321
+ raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
322
+
323
+ date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
324
+ elevation = elevation if elevation is not None else self.elevation
325
+
326
+ return dawn(
327
+ latitude=self.latitude,
328
+ longitude=self.longitude,
329
+ date=date,
330
+ twilight_type=twilight_type,
331
+ elevation=elevation,
332
+ timezone=self.tzinfo if local else timezone.utc,
333
+ with_refraction=True
334
+ )
335
+
336
+ def dusk(
337
+ self,
338
+ date: Optional[date] = None,
339
+ local: bool = True,
340
+ twilight_type: Literal["civil", "nautical", "astronomical"] = "civil",
341
+ elevation: Optional[float] = None
342
+ ) -> Optional[datetime]:
343
+ """🌙 Calcula anochecer (fin crepúsculo)."""
344
+ if local and self.timezone is None:
345
+ raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
346
+
347
+ date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
348
+ elevation = elevation if elevation is not None else self.elevation
349
+
350
+ return dusk(
351
+ latitude=self.latitude,
352
+ longitude=self.longitude,
353
+ date=date,
354
+ twilight_type=twilight_type,
355
+ elevation=elevation,
356
+ timezone=self.tzinfo if local else timezone.utc,
357
+ with_refraction=True
358
+ )
359
+
360
+ def midnight(
361
+ self,
362
+ date: Optional[date] = None,
363
+ local: bool = True
364
+ ) -> datetime:
365
+ """🌑 Calcula medianoche solar."""
366
+ if local and self.timezone is None:
367
+ raise ValueError("Se solicitó hora local pero no se definió una zona horaria.")
368
+
369
+ date = date or datetime.now(self.tzinfo if local else timezone.utc).date()
370
+
371
+ return midnight(
372
+ longitude=self.longitude,
373
+ date=date,
374
+ timezone=self.tzinfo if local else timezone.utc
375
+ )
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.5"
2
+ __version__ = "1.0.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solarmoonpy
3
- Version: 1.0.5
3
+ Version: 1.0.6
4
4
  Summary: Precise solar and lunar calculations for astronomical applications
5
5
  Author-email: figorr <jdcuartero@yahoo.es>
6
6
  License: Apache License
@@ -0,0 +1,10 @@
1
+ solarmoonpy/__init__.py,sha256=cS0ZjePNX75usK8mgvbk_uguVpXRixqZU4zjrEtg42c,722
2
+ solarmoonpy/location.py,sha256=XC-d0g2kjLdTJyYDokz8m3C5bKZ7h9xy6rsRf40uQ84,12764
3
+ solarmoonpy/moon.py,sha256=yhJSsED7AWK3nF79fjqFlCeqYjiXXFhqGE36Nyfp0qk,19699
4
+ solarmoonpy/sun.py,sha256=K31_btucloC5WYnltFb_X_3hF1Xk0_5aQ1hRSrYP7C8,13396
5
+ solarmoonpy/version.py,sha256=C1AoWYSu92cpPGAfSGtTeKE9EGde-yUoSv0cT_BVTJM,35
6
+ solarmoonpy-1.0.6.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
7
+ solarmoonpy-1.0.6.dist-info/METADATA,sha256=hBdLEmwc3QWazEVQKaWF63k6pMVrtUUmiLoshnV6nD4,15023
8
+ solarmoonpy-1.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ solarmoonpy-1.0.6.dist-info/top_level.txt,sha256=egsoDe9E0QEu5GfAA5jSvy_3qTCWMLMe_Kh3gnM6-dY,12
10
+ solarmoonpy-1.0.6.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,,