사이트 도구


지진_화산:지진

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판이전 판
다음 판
이전 판
지진_화산:지진 [2026/04/23 08:32] – [2. 관측 (Observation)] admin지진_화산:지진 [2026/05/06 08:37] (현재) – [3. 산출물 (Data Products)] admin
줄 1: 줄 1:
 ======지진 (Earthquake) 데이터====== ======지진 (Earthquake) 데이터======
  
-[[지진_화산:지진#활용방안|{{:wiki:활용방안클릭.png?linkonly250|}}]] +[[지진_화산:지진#활용방안|{{:wiki:활용방안클릭.png?250|}}]] 
  
 출처: https://www.kma.go.kr/eqk_pub/main2.do 출처: https://www.kma.go.kr/eqk_pub/main2.do
줄 16: 줄 16:
 국가지진관측망은 지면의 미세한 움직임을 실시간으로 포착하기 위해 다수의 관측소와 고정밀 센서로 구성됩니다. 국가지진관측망은 지면의 미세한 움직임을 실시간으로 포착하기 위해 다수의 관측소와 고정밀 센서로 구성됩니다.
    
- •  관측망 밀집도: 전국적으로 약 10~20km 간격의 관측망을 구축하여 지진 분석의 정밀도를 높이고 있습니다.+ •  **관측망 밀집도**: 전국적으로 약 10~20km 간격의 관측망을 구축하여 지진 분석의 정밀도를 높이고 있습니다.
  
- •  센서의 종류: + •  **센서의 종류**
-  *  속도계(Velocity Sensor): 지면 운동의 속도를 측정하며, 초광대역(VBB) 및 광대역 센서는 원거리 및 미세 지진 분석에, 단주기 센서는 근거리 지진 분석에 특화됩니다. +  *  **속도계(Velocity Sensor)**: 지면 운동의 속도를 측정하며, 초광대역(VBB) 및 광대역 센서는 원거리 및 미세 지진 분석에, 단주기 센서는 근거리 지진 분석에 특화됩니다. 
-  *  가속도계(Acceleration Sensor): 강지진 발생 시 포화(Saturation) 없이 강한 흔들림을 측정하며, 구조물 및 지반의 동적 특성 분석에 필수적입니다. +  *  **가속도계(Acceleration Sensor)**: 강지진 발생 시 포화(Saturation) 없이 강한 흔들림을 측정하며, 구조물 및 지반의 동적 특성 분석에 필수적입니다. 
-  *  관측 환경: 배경 잡음(Background Noise)을 최소화하기 위해 지표형 외에도 지하 수십~수백 미터 아래에 설치하는 시추형(Borehole) 관측소를 확대 운영하고 있습니다.+ •  **관측 환경**: 배경 잡음(Background Noise)을 최소화하기 위해 지표형 외에도 지하 수십~수백 미터 아래에 설치하는 시추형(Borehole) 관측소를 확대 운영하고 있습니다.
  
  
줄 28: 줄 28:
 =====3. 산출물 (Data Products)===== =====3. 산출물 (Data Products)=====
  
- • 파형 데이터(Waveform Data): 관측소에서 기록된 시계열 데이터로, 국제 표준 포맷인 MiniSEED 혹은 SEED 형식으로 제공됩니다. 이는 지각 구조 연구 및 파원을 정밀 분석하는 데 활용됩니다.+ • **파형 데이터(Waveform Data)**: 관측소에서 기록된 시계열 데이터로, 국제 표준 포맷인 MiniSEED 혹은 SEED 형식으로 제공됩니다. 이는 지각 구조 연구 및 파원을 정밀 분석하는 데 활용됩니다.
  
- • 지진 카탈로그(Event Catalog): 지진 발생 시각, 진앙 위치(위경도), 진원 깊이, 규모 등을 포함한 속성 데이터베이스입니다.+ • **지진 카탈로그(Event Catalog)**: 지진 발생 시각, 진앙 위치(위경도), 진원 깊이, 규모 등을 포함한 속성 데이터베이스입니다.
  
- • 지진조기경보(EEW): 지진 발생 후 수 초 이내에 파괴력이 큰 S파 도착 시간을 예측하여 전파하는 경보 데이터입니다.+ • **지진조기경보(EEW)**: 지진 발생 후 수 초 이내에 파괴력이 큰 S파 도착 시간을 예측하여 전파하는 경보 데이터입니다.
  
- • 계기진도 데이터: 관측된 최대지반가속도(PGA) 또는 속도(PGV)를 기반으로 산출된 지역별 진도 값(MMI 등급)입니다.+ • **계기진도 데이터**: 관측된 최대지반가속도(PGA) 또는 속도(PGV)를 기반으로 산출된 지역별 진도 값(MMI 등급)입니다.
  
 {{지진_화산:e2e557da9b204239c774af7fe58a06dae7iit1tkhydmy4mp-279.png?nolink800}} {{지진_화산:e2e557da9b204239c774af7fe58a06dae7iit1tkhydmy4mp-279.png?nolink800}}
줄 50: 줄 50:
 기상청은 울릉도, 임원항 및 정동진에 해일파고계(15m 이상 파고 관측 가능), 연안 방재 관측 시스템(17개소), 해상감시 CCTV(24개소), 국립해양조사원의 조위자료(56개소) 등을 공유하여 실시간으로 감시에 활용하고 있습니다. 기상청은 울릉도, 임원항 및 정동진에 해일파고계(15m 이상 파고 관측 가능), 연안 방재 관측 시스템(17개소), 해상감시 CCTV(24개소), 국립해양조사원의 조위자료(56개소) 등을 공유하여 실시간으로 감시에 활용하고 있습니다.
  
- • 해일파고계: 연안의 조석 성분을 제외한 해수면의 급격한 변동만을 추출하기 위해 초음파 혹은 수압 센서를 활용합니다.+ • **해일파고계**: 연안의 조석 성분을 제외한 해수면의 급격한 변동만을 추출하기 위해 초음파 혹은 수압 센서를 활용합니다.
  
- • 조위관측소 공유: 국립해양조사원의 고정밀 조위 관측 데이터를 실시간으로 수신하여 보완적인 감시망을 운영합니다.+ • **조위관측소 공유**: 국립해양조사원의 고정밀 조위 관측 데이터를 실시간으로 수신하여 보완적인 감시망을 운영합니다.
  
 {{지진_화산:0d63ac33-4724-4ea4-9b25-f9a54a7feffd.jpg?nolink500}} {{지진_화산:0d63ac33-4724-4ea4-9b25-f9a54a7feffd.jpg?nolink500}}
줄 59: 줄 59:
 =====3. 산출물 (Data Products)===== =====3. 산출물 (Data Products)=====
  
- • 수치모의 시나리오 DB: 가상 지진 파원에 따른 예상 파고 및 도달 시간을 미리 계산한 6,000여 개의 시나리오 세트입니다.+ • **수치모의 시나리오 DB**: 가상 지진 파원에 따른 예상 파고 및 도달 시간을 미리 계산한 6,000여 개의 시나리오 세트입니다.
  
- • 지진해일 특보: 예상 파고가 일정 기준(주의보 0.5m, 경보 1.0m 이상)을 초과할 것으로 예상될 때 발표되는 격자형 공간 데이터입니다.+ • **지진해일 특보**: 예상 파고가 일정 기준(주의보 0.5m, 경보 1.0m 이상)을 초과할 것으로 예상될 때 발표되는 격자형 공간 데이터입니다.
  
- • 실시간 해수면 변동 자료: 관측소별로 기록된 지진해일의 최대 파고와 내습 지속 시간 데이터입니다.+ • **실시간 해수면 변동 자료**: 관측소별로 기록된 지진해일의 최대 파고와 내습 지속 시간 데이터입니다.
  
 {{지진_화산:4f6b31d517c8401fc11367483f6fd152mWHLeeTs6IHCjIAP-673.png}} {{지진_화산:4f6b31d517c8401fc11367483f6fd152mWHLeeTs6IHCjIAP-673.png}}
  
  
-=====4. 활용방안=====+===== 4. 활용방안 ===== 
 소스코드 : {{ :지진_화산:earthquake_epicenter_example.py |}} 소스코드 : {{ :지진_화산:earthquake_epicenter_example.py |}}
  
-====① 데터 파싱 함수====+==== 4.1. 라브러리 설치 및 기본 설정 ==== 
 + 
 +지진목록 자료를 조회하고 최근 1년 지진의 진앙 분포도를 시각화하기 위해 필요한 라이브러리를 설치합니다. 
 <code py> <code py>
-   def parse_text_to_dataframe(text): +pip install requests pandas matplotlib cartopy
-       """ API 응답 텍스트를 DataFrame으로 변환 +
-       주의: LOC 뒤쪽 INT, REM, COR에는 쉼표가 섞일 수 있으므로 이번 교육용 지도 예제에서는 앞의 고정 7개 필드 + LOC만 사용한다.""" +
-       lines = extract_data_lines(text) +
-       if len(lines) == 0: +
-           return pd.DataFrame(columns=COLUMNS) +
-       rows = [] +
-       for line in lines: +
-           line = line.strip() +
-           # 맨 끝 ",=" 제거 +
-           if line.endswith(",="): +
-               line = line[:-2] +
-           # 앞 7개는 고정 필드, 나머지는 통째로 받음 +
-           parts = line.split(",", 7) +
-           if len(parts) < 8: +
-               continue +
-           tp = parts[0].strip() +
-           tm_fc = parts[1].strip() +
-           seq = parts[2].strip() +
-           tm_eqk_msc = parts[3].strip() +
-           mt = parts[4].strip() +
-           lat = parts[5].strip() +
-           lon = parts[6].strip() +
-           # 나머지 전체에서 LOC만 추출 +
-           rest = parts[7].strip() +
-           loc = rest.split(",", 1)[0].strip() +
-           rows.append([tp, tm_fc, seq, tm_eqk_msc, mt, lat, lon, loc]) +
-       if len(rows) == 0: +
-           return pd.DataFrame(columns=COLUMNS) +
-       df = pd.DataFrame(rows, columns=COLUMNS) +
-       return df+
 </code> </code>
-====② 전체 분포도 시각화 함수====      +
 <code py> <code py>
-   def plot_all_map(df): +import os 
-    """전체 지진 분포도 저장 규모 구간별 색상을 다르게 사용""" +from datetime import datetime, timedelta 
-       if df.empty: + 
-           print("[안내] 전체 지도에 그릴 데이터가 없습니다.") +import requests 
-           return +import pandas as pd 
-       fig, ax = setup_map() + 
-       tmp = df.copy() +import matplotlib 
-       tmp["MAG_CLASS"] = tmp["MT"].apply(classify_magnitude) +matplotlib.use("Agg") 
-       tmp["COLOR"] = tmp["MAG_CLASS"].apply(magnitude_class_to_color+import matplotlib.pyplot as plt 
-       ax.scatter(tmp["LON"], tmp["LAT"], s=POINT_SIZE, c=tmp["COLOR"], alpha=POINT_ALPHA, edgecolors=POINT_EDGE_COLOR, linewidths=POINT_EDGE_WIDTH,transform=ccrs.PlateCarree()) +from matplotlib import font_manager 
-       add_color_legend(ax) +from matplotlib.lines import Line2D 
-       ax.set_title("Epicenter Distribution for the Last 1 Year (All)", fontsize=14) + 
-       plt.subplots_adjust(right=0.82) +import cartopy.crs as ccrs 
-       plt.savefig(OUTPUT_PNG_ALL, dpi=150, bbox_inches="tight") +import cartopy.feature as cfeature
-       plt.close() +
-       print("[저장 완료] PNG:", OUTPUT_PNG_ALL)+
 </code> </code>
  
-====③ 예과====   +API 인증키와 API 주소, 조회 기간, 결과 파일명을 설정합니다. 
 + 
 +<code py> 
 +AUTH_KEY "본인의_API_인증키" 
 + 
 +BASE_URL "https://apihub.kma.go.kr/api/typ01/url/eqk_list.php" 
 + 
 +START_TM None 
 +END_TM None 
 + 
 +DISP = 1 
 +HELP = 1 
 + 
 +OUTPUT_CSV = "earthquake_list_last_1year.csv" 
 +RAW_TEXT_FILE = "earthquake_raw_response.txt" 
 + 
 +OUTPUT_PNG_ALL = "earthquake_epicenter_map_all.png" 
 +OUTPUT_PNG_M2 = "earthquake_epicenter_map_m2.png" 
 +OUTPUT_PNG_M4 = "earthquake_epicenter_map_m4.png" 
 +OUTPUT_PNG_M6 = "earthquake_epicenter_map_m6.png" 
 +OUTPUT_PNG_M8 = "earthquake_epicenter_map_m8.png" 
 +</code> 
 + 
 +BASE_URL은 (신)기자료개방포털 > API > 지진/화산 > 국내·외 지진정보 > 지진목록(임의기간의 지진정보) 조회 
 +메뉴의 호출 URL을 사용합니다. 
 + 
 +START_TM과 END_TM을 None으로 설정하면 현재 시각 기준 최근 1년 기간을 자동으로 조회합니다. 
 + 
 +==== 4.2. 지진목록 자료 조회 및 정리 ==== 
 + 
 +조회 시작시각과 종료시각을 yyyyMMddHHmm 형식으로 생성합니다. 
 + 
 +<code py> 
 +def make_time_range(): 
 +    if START_TM is not None and END_TM is not None: 
 +        return START_TM, END_TM 
 + 
 +    now = datetime.now() 
 +    one_year_ago = now - timedelta(days=365) 
 + 
 +    tm1 = one_year_ago.strftime("%Y%m%d%H%M"
 +    tm2 = now.strftime("%Y%m%d%H%M"
 +    return tm1, tm2 
 +</code> 
 + 
 +지진목록 API를 호출하여 응답 원문 TEXT를 받아옵니다. 
 + 
 +<code py> 
 +def fetch_earthquake_text(tm1, tm2): 
 +    params = { 
 +        "tm1": tm1, 
 +        "tm2": tm2, 
 +        "disp": DISP, 
 +        "help": HELP, 
 +        "authKey": AUTH_KEY, 
 +    } 
 + 
 +    response = requests.get(BASE_URL, params=params, timeout=60) 
 +    response.raise_for_status() 
 + 
 +    text = response.text 
 + 
 +    with open(RAW_TEXT_FILE, "w", encoding="utf-8") as f: 
 +        f.write(text) 
 + 
 +    return text 
 +</code> 
 + 
 +API 응답에는 설명 줄과 실제 데이터 줄이 함께 포함됩니다. 
 + 
 +설명 줄은 #으로 시작하므로 제외하고, 숫자로 시작하는 줄만 실제 지진 데이터로 사용합니다. 
 + 
 +<code py> 
 +def extract_data_lines(text): 
 +    lines = [] 
 + 
 +    for raw_line in text.splitlines(): 
 +        line = raw_line.strip() 
 + 
 +        if not line: 
 +            continue 
 + 
 +        if line.startswith("#"): 
 +            continue 
 + 
 +        if line[0].isdigit(): 
 +            lines.append(line) 
 + 
 +    return lines 
 +</code> 
 + 
 +응답 데이터 중 진앙 분포도 작성에 필요한 컬럼만 사용합니다. 
 + 
 +<code py> 
 +COLUMNS = [ 
 +    "TP", 
 +    "TM_FC", 
 +    "SEQ", 
 +    "TM_EQK_MSC", 
 +    "MT", 
 +    "LAT", 
 +    "LON", 
 +    "LOC", 
 +
 +</code> 
 + 
 +추출한 데이터 줄을 pandas DataFrame으로 변환합니다. 
 + 
 +<code py> 
 +def parse_text_to_dataframe(text): 
 +    lines = extract_data_lines(text) 
 + 
 +    if len(lines) == 0: 
 +        return pd.DataFrame(columns=COLUMNS) 
 + 
 +    rows = [] 
 + 
 +    for line in lines: 
 +        line = line.strip() 
 + 
 +        if line.endswith(",="): 
 +            line = line[:-2] 
 + 
 +        parts = line.split(",", 7) 
 + 
 +        if len(parts) < 8: 
 +            continue 
 + 
 +        tp = parts[0].strip() 
 +        tm_fc = parts[1].strip() 
 +        seq = parts[2].strip() 
 +        tm_eqk_msc = parts[3].strip() 
 +        mt = parts[4].strip() 
 +        lat = parts[5].strip() 
 +        lon = parts[6].strip() 
 + 
 +        rest = parts[7].strip() 
 +        loc = rest.split(",", 1)[0].strip() 
 + 
 +        rows.append([tp, tm_fc, seq, tm_eqk_msc, mt, lat, lon, loc]) 
 + 
 +    df = pd.DataFrame(rows, columns=COLUMNS) 
 +    return df 
 +</code> 
 + 
 +숫자형 컬럼과 시간 컬럼을 변환하고, 지도 표출에 필요한 규모, 위도, 경도 값이 없는 행은 제거합니다. 
 + 
 +<code py> 
 +def clean_dataframe(df): 
 +    if df.empty: 
 +        return df 
 + 
 +    df = df.copy() 
 + 
 +    numeric_cols = ["TP", "SEQ", "MT", "LAT", "LON"
 +    for col in numeric_cols: 
 +        df[col] = pd.to_numeric(df[col], errors="coerce"
 + 
 +    df["datetime_eqk"] = pd.to_datetime( 
 +        df["TM_EQK_MSC"], 
 +        format="%Y%m%d%H%M%S.%f", 
 +        errors="coerce" 
 +    ) 
 + 
 +    df["datetime_fc"] = pd.to_datetime( 
 +        df["TM_FC"], 
 +        format="%Y%m%d%H%M", 
 +        errors="coerce" 
 +    ) 
 + 
 +    df = df.dropna(subset=["MT", "LAT", "LON"]).copy() 
 +    df = df.sort_values("datetime_eqk").reset_index(drop=True) 
 + 
 +    return df 
 +</code> 
 + 
 +==== 4.3. 지진 진앙 분포도 저장 ==== 
 + 
 +지진 규모를 M2, M4, M6, M8 구간으로 분류합니다. 
 + 
 +<code py> 
 +def classify_magnitude(mt): 
 +    if pd.isna(mt): 
 +        return "UNKNOWN" 
 + 
 +    mt = float(mt) 
 + 
 +    if mt < 4.0: 
 +        return "M2" 
 +    elif mt < 6.0: 
 +        return "M4" 
 +    elif mt < 8.0: 
 +        return "M6" 
 +    else: 
 +        return "M8" 
 +</code> 
 + 
 +규모 구간별 색상을 지정합니다. 
 + 
 +<code py> 
 +COLOR_M2 = "#1E88E5" 
 +COLOR_M4 = "#FDD835" 
 +COLOR_M6 = "#FB8C00" 
 +COLOR_M8 = "#E53935" 
 + 
 +def magnitude_class_to_color(cls): 
 +    if cls == "M2": 
 +        return COLOR_M2 
 +    elif cls == "M4": 
 +        return COLOR_M4 
 +    elif cls == "M6": 
 +        return COLOR_M6 
 +    elif cls == "M8": 
 +        return COLOR_M8 
 +    else: 
 +        return "gray" 
 +</code> 
 + 
 +전지구 지도 배경을 생성합니다. 
 + 
 +<code py> 
 +def setup_map(): 
 +    fig = plt.figure(figsize=(16, 7)) 
 +    ax = plt.axes(projection=ccrs.PlateCarree()) 
 +    ax.set_global() 
 + 
 +    ax.coastlines(linewidth=0.8) 
 +    ax.add_feature(cfeature.BORDERS, linewidth=0.4) 
 +    ax.add_feature(cfeature.LAND, alpha=0.3) 
 +    ax.add_feature(cfeature.OCEAN, alpha=0.2) 
 +    ax.gridlines(draw_labels=False, linewidth=0.4, linestyle="--", alpha=0.5) 
 + 
 +    return fig, ax 
 +</code> 
 + 
 +전체 지진 진앙 분포도를 저장합니다. 
 + 
 +<code py> 
 +def plot_all_map(df): 
 +    if df.empty: 
 +        print("[안내] 전체 지도에 그릴 데이터가 없습니다."
 +        return 
 + 
 +    fig, ax = setup_map() 
 + 
 +    tmp = df.copy() 
 +    tmp["MAG_CLASS"] = tmp["MT"].apply(classify_magnitude) 
 +    tmp["COLOR"] = tmp["MAG_CLASS"].apply(magnitude_class_to_color) 
 + 
 +    ax.scatter( 
 +        tmp["LON"], 
 +        tmp["LAT"], 
 +        s=32, 
 +        c=tmp["COLOR"], 
 +        alpha=0.85, 
 +        edgecolors="black", 
 +        linewidths=0.25, 
 +        transform=ccrs.PlateCarree() 
 +    ) 
 + 
 +    ax.set_title("Epicenter Distribution for the Last 1 Year (All)", fontsize=14) 
 + 
 +    plt.savefig(OUTPUT_PNG_ALL, dpi=150, bbox_inches="tight"
 +    plt.close() 
 +</code> 
 + 
 +규모 구간별 지도도 각각 저장합니다. 
 + 
 +<code py> 
 +def plot_single_class_map(df, mag_class, output_png): 
 +    if df.empty: 
 +        return 
 + 
 +    tmp = df.copy() 
 +    tmp["MAG_CLASS"] = tmp["MT"].apply(classify_magnitude) 
 +    tmp = tmp[tmp["MAG_CLASS"] == mag_class].copy() 
 + 
 +    if tmp.empty: 
 +        return 
 + 
 +    color = magnitude_class_to_color(mag_class) 
 + 
 +    fig, ax = setup_map() 
 + 
 +    ax.scatter( 
 +        tmp["LON"], 
 +        tmp["LAT"], 
 +        s=32, 
 +        color=color, 
 +        alpha=0.85, 
 +        edgecolors="black", 
 +        linewidths=0.25, 
 +        transform=ccrs.PlateCarree() 
 +    ) 
 + 
 +    ax.set_title(f"Epicenter Distribution for the Last 1 Year ({mag_class})", fontsize=14) 
 + 
 +    plt.savefig(output_png, dpi=150, bbox_inches="tight"
 +    plt.close() 
 +</code> 
 + 
 +전체 실행 코드는 다음과 같습니다. 
 + 
 +<code py> 
 +tm1, tm2 = make_time_range() 
 + 
 +text = fetch_earthquake_text(tm1, tm2) 
 + 
 +df parse_text_to_dataframe(text) 
 + 
 +df clean_dataframe(df) 
 + 
 +df.to_csv(OUTPUT_CSV, index=False, encoding="utf-8-sig"
 + 
 +plot_all_map(df) 
 +plot_single_class_map(df, "M2", OUTPUT_PNG_M2) 
 +plot_single_class_map(df, "M4", OUTPUT_PNG_M4) 
 +plot_single_class_map(df, "M6", OUTPUT_PNG_M6) 
 +plot_single_class_map(df, "M8", OUTPUT_PNG_M8) 
 +</code> 
 + 
 +실행하면 다음 파일이 생성됩니다. 
 {{:지진_화산:earthquake_epicenter_map_all.png?nolink500}} {{:지진_화산:earthquake_epicenter_map_all.png?nolink500}}
 {{:지진_화산:earthquake_epicenter_map_m2.png?nolink500}} {{:지진_화산:earthquake_epicenter_map_m2.png?nolink500}}
줄 131: 줄 425:
 {{:지진_화산:earthquake_epicenter_map_m6.png?nolink500}} {{:지진_화산:earthquake_epicenter_map_m6.png?nolink500}}
 {{:지진_화산:earthquake_epicenter_map_m8.png?nolink500}} {{:지진_화산:earthquake_epicenter_map_m8.png?nolink500}}
- 
-