티스토리 뷰
어떤 솔루션이 일출, 일몰 시간을 기반으로 판단할 때 서버에서 일출/일몰 시간이 필요한데요. 다만 각 나라별로 일출, 일몰의 시간이 다르기 때문에 정확한 값은 일출, 일몰 시간을 제공해주는 자료를 통해서 확인 할 수 있습니다.
제공하는 API 사용하는 방법
https://sunrise-sunset.org/api
바로 위 사이트에서 제공하는 API를 사용하면 되는데요. 해당 서버에다가 요청하고 결과를 받는 REST 방식이다 보니 500ms 정도의 시간이 필요합니다. 우리가 만들 서버의 경우 이 정보를 받아와서 DB에 접근하고 어떠한 계산을 하게되면 기본적으로 500ms 를 초과하는 요청이 될 것인데 1회 요청에의 응답속도가 이렇게 느리다면 클라이언트에서는 굉장히 답답함을 느낄 수 있을 것입니다. 오늘 소개해드릴 일출, 일몰 시간에 대한 것은 GPS 정보 기반으로 계산하는 방법입니다.
- lat (float): Latitude in decimal degrees. Required.
- lng (float): Longitude in decimal degrees. Required.
- date (string): Date in YYYY-MM-DD format. Also accepts other date formats and even relative date formats. If not present, date defaults to current date. Optional.
- callback (string): Callback function name for JSONP response. Optional.
- formatted (integer): 0 or 1 (1 is default). Time values in response will be expressed following ISO 8601 and day_length will be expressed in seconds. Optional.
사용 예
https://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400&date=2023-1-16
결과
{"results":{"sunrise":"7:27:30 AM","sunset":"5:27:11 PM","solar_noon":"12:27:21 PM","day_length":"09:59:41","civil_twilight_begin":"7:00:50 AM","civil_twilight_end":"5:53:51 PM","nautical_twilight_begin":"6:29:04 AM","nautical_twilight_end":"6:25:37 PM","astronomical_twilight_begin":"5:58:02 AM","astronomical_twilight_end":"6:56:39 PM"},"status":"OK"}
오늘은 아침 7시27분30초에 해가 뜨고, 5시27분 11초에 해가지네요, 지금 글을 쓰는 시각은 저녁 6시를 막 넘었으니, 앗 집에가야 겠군요!
직접 계산하는 방법
이론
noaa.gov (미국 해양대기청)에서 낸 공식 자료로, 위도, 경도, 타임존 입력값을 통해 정확한 일출 일몰 시간을 구하는 수학식을 기술해놓았습니다.
구현
from math import pi, sin, cos, tan, acos, asin, atan, atan2
from datetime import datetime
import time
HH = 0
MM = 1
SUN_DIAMETER = 0.53
AIR_REF = (34.0/60.0)
def is_leap_year(year):
if year % 4 == 0 and year % 100 == 0 and year % 400 != 0:
return False
elif year % 4 == 0 and year % 100 != 0:
return True
elif year % 4 == 0 and year % 100 == 0 and year % 400 == 0:
return True
else:
return False
def get_julian_day(year, month, day):
tmp = -7.0 * (float(year) + (float(month) + 9.0) / 12.0) / \
4.0 + 275.0 * float(month) / 9.0 + float(day)
tmp += float(year * 367)
return (tmp - 730531.5 + 12.0 / 24.0)
def get_range_radian(x):
b = float(x / (2*pi))
a = float((2*pi) * (b - int(b)))
if a < 0:
a = (2*pi) + a
return a
def get_ha(lat, decl):
dfo = pi/180.0 * (0.5 * SUN_DIAMETER + AIR_REF)
if lat < 0.0:
dfo = -dfo
fo = tan(decl + dfo) * tan(lat * pi / 180.0)
if fo > 1.0:
fo = 1.0
fo = asin(fo) + pi / 2.0
return fo
def get_sun_longitude(days):
longitude = get_range_radian(
280.461 * pi / 180.0 + 0.9856474 * pi/180.0 * days)
g = get_range_radian(357.528 * pi/180.0 + 0.9856003 * pi/180.0 * days)
return get_range_radian(longitude + 1.915 * pi/180.0 * sin(g) + 0.02 * pi/180.0 * sin(2*g)), longitude
def convert_dtime_to_rtime(dhour):
hour = int(dhour)
minute = int((dhour - hour) * 60)
return hour, minute
def calculate_sunset_sunrise(latitude, longitude, timezone):
today = datetime.today()
days = get_julian_day(today.year, today.month, today.day)
gamma, mean_longitude = get_sun_longitude(days)
obliq = 23.439 * pi/180.0 - 0.0000004 * pi/180.0 * days
alpha = atan2(cos(obliq)*sin(gamma), cos(gamma))
delta = asin(sin(obliq)*sin(gamma))
ll = mean_longitude - alpha
if mean_longitude < pi:
ll += (2*pi)
eq = 1440.0 * (1.0 - ll / (2*pi))
ha = get_ha(latitude, delta)
sunrise = 12.0 - 12.0 * ha/pi + timezone - longitude/15.0 + eq/60.0
sunset = 12.0 + 12.0 * ha/pi + timezone - longitude/15.0 + eq/60.0
if sunrise > 24.0:
sunrise -= 24.0
if sunset > 24.0:
sunset -= 24.0
sunrise_time = [0, 0]
sunset_time = [0, 0]
sunrise_time[HH], sunrise_time[MM] = convert_dtime_to_rtime(sunrise)
sunset_time[HH], sunset_time[MM] = convert_dtime_to_rtime(sunset)
ret_sunrise, ret_sunset = "", ""
if sunrise_time[HH] < 10:
ret_sunrise += "0"
ret_sunrise += str(sunrise_time[HH])
ret_sunrise += ":"
if sunrise_time[MM] < 10:
ret_sunrise += "0"
ret_sunrise += str(sunrise_time[MM]-1)
if sunset_time[HH] < 10:
ret_sunset += "0"
ret_sunset += str(sunset_time[HH])
ret_sunset += ":"
if sunset_time[MM] < 10:
ret_sunset += "0"
ret_sunset += str(sunset_time[MM]+1)
return ret_sunrise, ret_sunset
if __name__ == '__main__':
s = time.time()
sunrise, sunset = calculate_sunset_sunrise(37.5642135, 127.0016985, 9)
# sunrise, sunset = calculate_sunset_sunrise(-33.8674769, 151.2069776, 11)
print("Sunrise:", sunrise)
print("Sunset:", sunset)
e = time.time()
print("time:", (e-s)*1000, "ms")
위 코드와 같이 calculate_sunset_sunrise는 위도와 경도 값 그리고 타임존 시간 값을 기반으로 계산되어 집니다. 물론 그 안에 들어가면 날짜정보도 필요하구요. 편의상 오늘 날짜를 넣었습니다. 그리고 식에 따라서 계산합니다. 이렇게 했을 때 실행시간은 0.13ms 걸렸기 때문에 API를 사용했을때 500ms 에 비행 엄청나게 빠른 레이턴시를 나타냅니다.
어떤 방식으로 하던 굉장히 틀린 결과를 내지는 않습니다. 일출/일몰의 시간이 정말 1초도 차이나는 서비스를 만들지 않는 이상 어느정도의 오차는 크게 영향이 없을 것인데, 혹시나 자신의 서비스가 지향하는 방식은 무엇으로 해야하는지 상황에 맞게 API를 가져다 쓰거나, 직접 계산해서 사용하시면 됩니다.
'Technology > Algorithm and Design' 카테고리의 다른 글
Go언어에서 Clean Architecture의 정의 및 적용 준비 (0) | 2023.03.20 |
---|---|
[디자인패턴] 어답터 패턴의 장단점, 어디에 적용할까? (0) | 2023.02.21 |
조합? (1) | 2016.06.11 |
소수구하기 - 에라토스테네스의 체 (0) | 2016.05.22 |
카프리카 상수 구하기 (0) | 2016.04.24 |