wing-ops/prediction/scat/esi_mapper.py
leedano d9fb4506bc feat(scat): Pre-SCAT 관할서 필터링 + 해안조사 데이터 파이프라인 구축
- 백엔드: 관할서 목록 API, zone 필터링 쿼리 추가
- 프론트: ScatLeftPanel 관할서 드롭다운, ScatMap/ScatPopup 개선
- 기상탭: WeatherRightPanel 리팩토링
- prediction/scat: PDF 파싱 → 지오코딩 → ESI 매핑 파이프라인
- vite.config: proxy 설정 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:53:19 +09:00

66 lines
2.3 KiB
Python

"""cst_tp_cd(해안 구성) → ESI 등급 매핑.
ESI(Environmental Sensitivity Index) 등급은 해안의 물리적 특성에 따라 분류된다.
매핑 기준: NOAA(2010) + 성 등(2003) ESI 등급 표.
"""
from __future__ import annotations
import re
from typing import Optional, Tuple
# 키워드 → (esi_cd, esi_num) 매핑 (구체적 키워드 우선)
_ESI_RULES: list[tuple[list[str], str, int]] = [
# 8B: 습지
(['염습지', '습지'], '8B', 8),
# 8A: 갯벌/점토질
(['갯벌', '점토질', '점토'], '8A', 8),
# 7: 반폐쇄형
(['반폐쇄', '반 폐쇄'], '7', 7),
# 6B: 투과성 인공호안/사석
(['투과성 인공호안', '투과성 사석', '투과성 인공해안', '투과성 경사식'], '6B', 6),
# 6A: 자갈/바위
(['자갈', '왕자갈', '바위', '갈('], '6A', 6),
# 5: 모래+자갈 혼합
(['모래자갈', '모래+자갈', '혼합'], '5', 5),
# 4: 굵은 모래
(['굵은 모래', '조립질 모래'], '4', 4),
# 3: 세립질 모래/모래(기본)
(['세립질 모래', '세립질', '모래'], '3', 3),
# 2: 수평암반/비투과성
(['수평암반', '수평호안', '기반암', '비투과성 기질', '비투과성 인공호안',
'비투과성 인공해안', '노출기반암', '수암반', '콘크리트'], '2', 2),
# 1: 수직암반/인공구조물/계류안벽
(['수직암반', '인공구조물', '직립호안', '절벽', '수직호안', '수진호안',
'수직 계류', '인공호안', '계류 안벽', '안벽'], '1', 1),
]
# ESI 코드 → 숫자 변환
_ESI_NUM_RE = re.compile(r'^(\d+)')
def map_esi(cst_tp_cd: Optional[str]) -> Tuple[Optional[str], Optional[int]]:
"""cst_tp_cd(해안 구성 키워드)에서 ESI 등급을 매핑한다.
Returns:
(esi_cd, esi_num) 튜플. 매핑 실패 시 (None, None).
"""
if not cst_tp_cd:
return None, None
text = cst_tp_cd.strip()
for keywords, esi_cd, esi_num in _ESI_RULES:
for kw in keywords:
if kw in text:
return esi_cd, esi_num
return None, None
def parse_esi_cd(esi_str: str) -> Tuple[str, int]:
"""ESI 등급 문자열(e.g. '8A', '6B', '2')에서 (esi_cd, esi_num) 추출."""
esi_cd = esi_str.strip()
m = _ESI_NUM_RE.match(esi_cd)
esi_num = int(m.group(1)) if m else 0
return esi_cd, esi_num