사이트 도구


지진_화산:지진

문서의 이전 판입니다!


지진 (Earthquake) 데이터

1. 개요

지진은 지구 내부 단층의 급격한 파쇄 혹은 마그마의 이동으로 발생하는 탄성파 현상입니다. 한반도는 유라시아 판 내부에 위치하여 판 경계 지역(일본 등)에 비해 발생 빈도는 낮으나, 지각 내 응력 축적에 따른 중대형 지진(규모 5.0 이상)의 위험이 상존합니다. 기상청은 국가지진방재의 중추로서 지진의 발생 위치(진원, 진앙), 발생 시각, 규모(Magnitude), 그리고 지표면의 흔들림 정도인 진도(Intensity)를 정밀하게 분석하여 데이터를 제공합니다.

출처: https://www.kma.go.kr/eqk_pub/obsrEarthquake.do?tab=3

2. 관측 (Observation)

국가지진관측망은 지면의 미세한 움직임을 실시간으로 포착하기 위해 다수의 관측소와 고정밀 센서로 구성됩니다.

관측망 밀집도: 전국적으로 약 10~20km 간격의 관측망을 구축하여 지진 분석의 정밀도를 높이고 있습니다.

센서의 종류:

  • 속도계(Velocity Sensor): 지면 운동의 속도를 측정하며, 초광대역(VBB) 및 광대역 센서는 원거리 및 미세 지진 분석에, 단주기 센서는 근거리 지진 분석에 특화됩니다.
  • 가속도계(Acceleration Sensor): 강지진 발생 시 포화(Saturation) 없이 강한 흔들림을 측정하며, 구조물 및 지반의 동적 특성 분석에 필수적입니다.

관측 환경: 배경 잡음(Background Noise)을 최소화하기 위해 지표형 외에도 지하 수십~수백 미터 아래에 설치하는 시추형(Borehole) 관측소를 확대 운영하고 있습니다.

3. 산출물 (Data Products)

• 파형 데이터(Waveform Data): 관측소에서 기록된 시계열 데이터로, 국제 표준 포맷인 MiniSEED 혹은 SEED 형식으로 제공됩니다. 이는 지각 구조 연구 및 파원을 정밀 분석하는 데 활용됩니다.

• 지진 카탈로그(Event Catalog): 지진 발생 시각, 진앙 위치(위경도), 진원 깊이, 규모 등을 포함한 속성 데이터베이스입니다.

• 지진조기경보(EEW): 지진 발생 후 수 초 이내에 파괴력이 큰 S파 도착 시간을 예측하여 전파하는 경보 데이터입니다.

• 계기진도 데이터: 관측된 최대지반가속도(PGA) 또는 속도(PGV)를 기반으로 산출된 지역별 진도 값(MMI 등급)입니다.

지진해일 (Tsunami) 데이터

1. 개요

지진해일은 해저 지진 등으로 인해 해수면 높이가 급격히 변하며 발생하는 장주기파(Long Wave)입니다. 동해와 같이 수심이 깊은 해역에서 발생한 지진해일은 시속 수백 킬로미터로 이동하며, 연안의 얕은 수심으로 진입할 때 파고가 급증하여 막대한 피해를 입힙니다. 기상청은 일본 서쪽 해역의 대규모 지진에 따른 동해안 내습 가능성을 상시 감시합니다.

ecece1e04cbacfc0a6efa11338222dbf_1628170432_1722.jpg

2. 관측 (Observation)

기상청은 울릉도, 임원항 및 정동진에 해일파고계(15m 이상 파고 관측 가능), 연안 방재 관측 시스템(17개소), 해상감시 CCTV(24개소), 국립해양조사원의 조위자료(56개소) 등을 공유하여 실시간으로 감시에 활용하고 있습니다.

• 해일파고계: 연안의 조석 성분을 제외한 해수면의 급격한 변동만을 추출하기 위해 초음파 혹은 수압 센서를 활용합니다.

• 조위관측소 공유: 국립해양조사원의 고정밀 조위 관측 데이터를 실시간으로 수신하여 보완적인 감시망을 운영합니다.

0d63ac33-4724-4ea4-9b25-f9a54a7feffd.jpg 00f9dc56-6094-4a1b-b67d-847c24c05194.jpg

3. 산출물 (Data Products)

• 수치모의 시나리오 DB: 가상 지진 파원에 따른 예상 파고 및 도달 시간을 미리 계산한 6,000여 개의 시나리오 세트입니다.

• 지진해일 특보: 예상 파고가 일정 기준(주의보 0.5m, 경보 1.0m 이상)을 초과할 것으로 예상될 때 발표되는 격자형 공간 데이터입니다.

• 실시간 해수면 변동 자료: 관측소별로 기록된 지진해일의 최대 파고와 내습 지속 시간 데이터입니다.

4. 활용방안

4.1. 라이브러리 설치 및 기본 설정

지진목록 자료를 조회하고 최근 1년 지진의 진앙 분포도를 시각화하기 위해 필요한 라이브러리를 설치합니다.

pip install requests pandas matplotlib cartopy
import os
from datetime import datetime, timedelta
 
import requests
import pandas as pd
 
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib import font_manager
from matplotlib.lines import Line2D
 
import cartopy.crs as ccrs
import cartopy.feature as cfeature

API 인증키와 API 주소, 조회 기간, 결과 파일명을 설정합니다.

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"

BASE_URL은 (신)기상자료개방포털 > API > 지진/화산 > 국내·외 지진정보 > 지진목록(임의기간의 지진정보) 조회 메뉴의 호출 URL을 사용합니다.

START_TM과 END_TM을 None으로 설정하면 현재 시각 기준 최근 1년 기간을 자동으로 조회합니다.

4.2. 지진목록 자료 조회 및 정리

조회 시작시각과 종료시각을 yyyyMMddHHmm 형식으로 생성합니다.

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

지진목록 API를 호출하여 응답 원문 TEXT를 받아옵니다.

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

API 응답에는 설명 줄과 실제 데이터 줄이 함께 포함됩니다.

설명 줄은 #으로 시작하므로 제외하고, 숫자로 시작하는 줄만 실제 지진 데이터로 사용합니다.

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

응답 데이터 중 진앙 분포도 작성에 필요한 컬럼만 사용합니다.

COLUMNS = [
    "TP",
    "TM_FC",
    "SEQ",
    "TM_EQK_MSC",
    "MT",
    "LAT",
    "LON",
    "LOC",
]

추출한 데이터 줄을 pandas DataFrame으로 변환합니다.

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

숫자형 컬럼과 시간 컬럼을 변환하고, 지도 표출에 필요한 규모, 위도, 경도 값이 없는 행은 제거합니다.

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

4.3. 지진 진앙 분포도 저장

지진 규모를 M2, M4, M6, M8 구간으로 분류합니다.

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"

규모 구간별 색상을 지정합니다.

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"

전지구 지도 배경을 생성합니다.

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

전체 지진 진앙 분포도를 저장합니다.

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()

규모 구간별 지도도 각각 저장합니다.

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()

전체 실행 코드는 다음과 같습니다.

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)

실행하면 다음 파일이 생성됩니다.