레이블이 공공데이터인 게시물을 표시합니다. 모든 게시물 표시
레이블이 공공데이터인 게시물을 표시합니다. 모든 게시물 표시

2016년 1월 5일 화요일

기상청 날씨 API 개선방안

 기상청 날씨 서비스를 위한 성능 개선 방안을 다룬다.


먼저 기본적인 사용 방법이다.


1. “위/경도” 또는 “지명 이름”를 “격좌 좌표”로 변한
2. “격좌 좌표”를 기상청 API에 조회 (날씨)

조회가 발생할 때마다 기상청 API를 호출하는 것은 비효율적이다.


다음은 성능 개선 방안이다.


1. “지역 이름”에 해당하는 “날씨 정보”를 일괄 수집한다. (총 3772, 시단위 84)
=> 수집된 데이터는 “지역 이름”, “위/경도”, “격자 좌표”,  “날씨 정보” 속성을 가진다.
2. 위/경도를 역지오코딩으로 “지역 이름”을 추출한다.
3. “지역 이름”에 해당되는 “날씨 정보”를 반환한다.



만들어 놓은 것만 쓰다보니 그림과 같은 1차 발상을 한다.
역지오코딩을 통해 "지역 이름"을 추출하는 것이 API 서비스를 통해서 다음/네이버에서 제공된다. 속도가 느린 것이 당연한 API 서비스다.!!

더 낳은 방안을 찾다.


날씨에 중요한 건 “지역 이름”이 아니라 관측 장소와 거리이다.
“위/경도” 좌표 기준으로 최단 거리에 해당하는 지역의 "날씨 정보"를 반환하자.

최단 거리는 min() 구하는 로직이라 시간 복잡도 O(n)으로 충분하다.

사무실에 가만히 공상하는 것이 낯선 환경에서는 생각은 짧고 손은 분주하다.


검증을 위해 구현한 예제 코드이다.

import pandas as pd
import sqlite3
conn = sqlite3.connect('kma.db')
df = pd.read_sql('select * from loc_map_book',con=conn)

df = df [ df['lev3'] == '-' ]
df['city'] = df['lev2'].apply(lambda i: i[-1] == '시' or i[-1] == '-')
TPS = df[ df['city'] ].to_dict(orient = 'records')

df.head(3)
OUT>>

lev1
lev2
lev3
nx
ny
lon
lat
city
0
서울특별시
-
-
60
127
126.980008
37.563569
TRUE
1
서울특별시
종로구
-
60
127
126.981642
37.570378
FALSE
19
서울특별시
중구
-
60
127
126.999642
37.561003
FALSE

def closest_pair(lon,lat):
    _min_dist = pow(TPS[0]['lon'] - lon, 2) + pow(TPS[0]['lat'] - lat, 2)
    
    for tp in TPS[1:]:
        _cur_dist = pow( tp['lon'] - lon,2 ) + pow( tp['lat'] - lat, 2 )
        
        if _cur_dist < _min_dist:
            _min_dist = _cur_dist
            _out_pair = tp
    
    return _out_pair

closest_pair(132,37) # 울릉도
OUT>>
{'city': True,
 'lat': 37.44708611111111,
 'lev1': '강원도',
 'lev2': '삼척시',
 'lev3': '-',
 'lon': 129.16748888888887,
 'nx': 98,
 'ny': 125}

울릉도의 경우 가장 가까운 "강원도 삼척시"가 나온다.



2015년 12월 18일 금요일

동네 예보 좌표 변환용 C 코드 변환

공공 데이터(동네 예보) 개발 가이드에 보면 위/경도 좌표를 격자 좌표로 변환 하는 C 코드가 있다.
바이너리로 만들어 subprocess로 호출 해서 해도 되지만 OS 환경에 따라 빌드해야 하는 번거로움이 있다.
이에, 파이썬 코드로 변환했다.

변환 과정에서 주의 사항은 다음과 같다.

math.pow()의 경우 결과를 float 타입으로 변경하는 과정이 있고, 여기서 복소수 같은 복합 연산 결과를 만났을때, 에러를 일으킬 수 있다.
So, 기본적으로 제공하는 pow 함수를 사용하면, 결과를 그대로 넘겨 준다.

math.pow 사용 예제
In [7]: math.pow(-2.6050890646938014, 0.715566847180628)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-7-7fb3d71c4e22> in <module>()
----> 1 math.pow(-2.6050890646938014, 0.715566847180628)
ValueError: math domain error

pow 사용 예제
In [8]: pow(-2.6050890646938014, 0.715566847180628)
Out[8]: (-1.2432573697690525+1.5461879897834474j)

In [9]: type(pow(-2.6050890646938014, 0.715566847180628))
Out[9]: complex

* complex(복소수)의 경우, real(실수)만 뽑아서 사용하면 된다.

코드가 하는 역활에 대해서 찾아 봤다.

지도 투영법은 위경도로 이루어진 가상의 좌표를 평면상에 옮기는 방법이다.
지도 투영법 중에 람베르트 정각원추도법(람베르트正角圓錐圖法, Lambert conformal conic projection)
을 사용한다.

마지막으로 실제 변경된 코드이다.

격자 좌표에서 위/경도 좌표로 변환하는 역방향은 제외 했다.
from math import asin, sin, cos, tan, log

NX = 149  # X축 격자점 수
NY = 253  # Y축 격자점 수


def main(lon, lat):
    # 동네예보 지도 정보
    class map:
        pass

    map.Re = 6371.00877  # 지도반경
    map.grid = 5.0  # 격자간격 (km)
    map.slat1 = 30.0  # 표준위도 1
    map.slat2 = 60.0  # 표준위도 2
    map.olon = 126.0  # 기준점 경도
    map.olat = 38.0  # 기준점 위도
    map.xo = 210 / map.grid  # 기준점 X좌표
    map.yo = 675 / map.grid  # 기준점 Y좌표

    return map_conv(lon, lat, map)  # x, y


# 좌표 변환
def map_conv(lon, lat, map):
    _x, _y = lamcproj(lon, lat, map)
    _x = int(_x + 1.5)
    _y = int(_y + 1.5)
    return _x, _y


# Lambert Conformal Conic Projection
def lamcproj(lon, lat, map):
    PI = asin(1.0) * 2.0
    DEGRAD = PI / 180.0

    re = map.Re / map.grid
    slat1 = map.slat1 * DEGRAD
    slat2 = map.slat2 * DEGRAD
    olon = map.olon * DEGRAD
    olat = map.olat * DEGRAD

    sn = tan(PI * 0.25 + slat2 * 0.5) / tan(PI * 0.25 + slat1 * 0.5)
    sn = log(cos(slat1) / cos(slat2)) / log(sn)
    sf = tan(PI * 0.25 + slat1 * 0.5)
    sf = pow(sf, sn) * cos(slat1) / sn
    ro = tan(PI * 0.25 + olat * 0.5)
    ro = re * sf / pow(ro, sn)

    ra = tan(PI * 0.25 + lat * DEGRAD * 0.5)
    ra = re * sf / pow(ra, sn)
    theta = lon * DEGRAD - olon
    if theta > PI:
        theta -= 2.0 * PI
    if theta < -PI:
        theta += 2.0 * PI
    theta *= sn
    x = ((ra * sin(theta)) + map.xo).real
    y = ((ro - ra * cos(theta)) + map.yo).real
    return x, 


커버 사진

2015년 11월 26일 목요일

접속 지역 날씨 정보 추출 방법

사이트에 접속하는 사용자의 날씨 정보를 추출하는 방법을 다룬다.

정보 추출은 다음 두가지 절차가 있다.

1. IP 주소(Address)나 브라우저(Browser)로 부터 위치정보 가져오기
2. 위치정보로 날씨 정보 가져 오기


* 위치정보 : 지역이름이나 GPS 좌표 사용


몇 가지 주요 사이트를 대상으로 테이블로 정리한다. (2015년 11월 25일 기준)

분류 구분 특징 비용 비고
IP 위치코드 변환 MaxMind (GeoIP) 인지도 및 완성도 높음
API 문서 수준이 높음
로컬 DB 서비스(성능)
50,000 조회 20달러
DB 다운로드 초기 370달러
(업데이트 100달러/달)
KR 정확도 66%
IP-API REST API 제공 무료 : 분당 250
무제한 : 3달 45유로
KR 정확도 높음 (경험)
W3C 브라우저 레벨의 위치정보 사용 여부 확인
응답 데이터가 GPS 정보(Only)
무료 W3C 협회 표준
위치코드 날씨 변환 Yahoo 손쉬운 사용
단지 비 상업적으로 사용 가능
무료
 - 일당 : 2,000

OpenWeatherMap GPS 좌표 및 지역명 처리
손쉬운 사용
무료
 - 분당 : 60
 - 일당 : 50,000

공공 데이터 일일 트래픽 : 3000 제한
실황 조회 : 1000 제한
활용사례 등록 시, 100,000 까지 확대 가능
무료 위경도 좌표를 격자형(X,Y)로 변형 해서 사용

* MaxMind는 Trial Key를 발급 받아서 테스트 가능하다. 한국 주소에 대한 정확도가 무척 떨어진다.


공공 데이터(동네예보정보조회서비스) 가이드가 좀 ?해서, 절차를 요약한다.

1. 지역정보는 위경도 정보로 변경한다.
2. 위경도를 X,Y 형식의 좌표로 변환하는 C 소스를 컴파일 해서 사용한다.
3. API에 입력되는 위치값 (nx, ny)는 X,Y 형식의 좌표값을 입력한다. (Not 위경도)

* 위도 (Latitude), 경도 (Longitude)



각 사이트 API에 대한 샘플 코드 및 Json 형식의 결과는 다음과 같다.

 - https://bitbucket.org/snippets/juhoon26/AAR4r


2015년 7월 27일 월요일

전체 사망자 대비 성별/연령별 자살 사망자 비율

국가통계포털(KOSIS)를 뒤적거리다 움찔한 자료가 있어서 오려 봅니다.
(현재 기준으로 가장 최신 자료)

* 2013년 연령별 사망자 수

* 2013년 연령별 전체 사망자 대비 자살 사망자 비율

평소 청소년 자살과, 고령화로 인한 노년층 자살을 많이 다루지만,
비율로 보면 사회에 집입하고 자리를 잡아가는 계층 전반에 걸쳐 있는 것을 추정해 볼 수 있다.
특히 "25세 ~ 30세"에 사망한 여성분들의 자살 비중이 50%가 넘었다. 

최근 TV에 "인문학 강의"가 많은 것이 나름 언론의 노력으로 추정해 보지만, 
교육을 통한 개인의 책임을 강조하는 것보다,
실질적인 환경 개선에 사회 구성원 모두의 노력이 필요해 보인다.

버스에서 블로깅 중인데, (세월호 사건을 계기로 잠시 개선을 보였던) "고속도로 진입 시에 안전벨트하기"가 롤백된 느낌이다.
탑승 가능 자석수 표지판을 달았으니 나머지는 개인의 선택인 걸까?