solarfit-api / api /clients /vworld_data.py
LIM
perf: reduce MAX_BASE_TILES=8, mini-batch inference
0939b04
Raw
History Blame Contribute Delete
3.59 kB
"""VWorld Data API 2.0 ν΄λΌμ΄μ–ΈνŠΈ β€” geomFilter=BOX() 방식."""
import re
import httpx
from typing import Optional
from api.config import VWORLD_API_KEY, VWORLD_DATA_URL, VWORLD_DOMAIN
_JIMOK: dict[str, str] = {
"μ „": "μ „", "λ‹΅": "λ‹΅", "κ³Ό": "κ³Όμˆ˜μ›", "λͺ©": "λͺ©μž₯μš©μ§€",
"μž„": "μž„μ•Ό", "κ΄‘": "κ΄‘μ²œμ§€", "μ—Ό": "μ—Όμ „", "λŒ€": "λŒ€μ§€",
"곡": "곡μž₯μš©μ§€", "ν•™": "ν•™κ΅μš©μ§€", "μ£Ό": "μ£Όμ°¨μž₯",
"유": "μœ μ›μ§€", "μ’…": "μ’…κ΅μš©μ§€", "사": "사적지",
"묘": "λ¬˜μ§€", "작": "μž‘μ’…μ§€", "도": "λ„λ‘œ",
"μ² ": "μ² λ„μš©μ§€", "제": "제방", "ν•˜": "ν•˜μ²œ",
"ꡬ": "ꡬ거", "μ–‘": "μ–‘μ–΄μž₯", "수": "μˆ˜λ„μš©μ§€",
"곡원": "곡원", "체": "μ²΄μœ‘μš©μ§€",
}
def _parse_jimok(jibun: str) -> tuple[str | None, str | None]:
"""'161λŒ€' β†’ ('λŒ€', 'λŒ€μ§€') ν˜•νƒœλ‘œ μ§€λͺ©μ½”λ“œ/이름 νŒŒμ‹±."""
m = re.search(r'([κ°€-힣]+)$', jibun or '')
if not m:
return None, None
code = m.group(1)
return code, _JIMOK.get(code, code)
_DELTA = 0.001 # ~111m bbox half-width
def _box_filter(lat: float, lon: float, delta: float = _DELTA) -> str:
return f"BOX({lon-delta},{lat-delta},{lon+delta},{lat+delta})"
def _get(data: str, geom_filter: str, attr_filter: str = "", size: int = 5) -> list[dict]:
if not VWORLD_API_KEY:
return []
params = {
"service": "data",
"request": "GetFeature",
"data": data,
"key": VWORLD_API_KEY,
"domain": VWORLD_DOMAIN,
"format": "json",
"size": str(size),
"page": "1",
"geometry": "true",
"attribute": "true",
"crs": "EPSG:4326",
"geomFilter": geom_filter,
}
if attr_filter:
params["attrFilter"] = attr_filter
try:
r = httpx.get(VWORLD_DATA_URL, params=params, timeout=10)
r.raise_for_status()
body = r.json()
fc = (
body.get("response", {})
.get("result", {})
.get("featureCollection", {})
)
return fc.get("features", [])
except (httpx.TimeoutException, httpx.RequestError, httpx.HTTPStatusError, ValueError, KeyError):
return []
def fetch_parcel_at(lat: float, lon: float) -> Optional[dict]:
"""μ’Œν‘œμ—μ„œ κ°€μž₯ κ°€κΉŒμš΄ ν•„μ§€ λ°˜ν™˜ (LP_PA_CBND_BUBUN)."""
features = _get("LP_PA_CBND_BUBUN", _box_filter(lat, lon))
if not features:
return None
props = features[0].get("properties", {})
jimok_code, jimok_name = _parse_jimok(props.get("jibun", ""))
return {
"pnu": props.get("pnu"),
"jimok": jimok_code,
"jimok_name": jimok_name,
"area_m2": None, # LP_PA_CBND_BUBUN λ ˆμ΄μ–΄μ— 면적 ν•„λ“œ μ—†μŒ
"address": props.get("addr"),
}
def fetch_regulatory_zones(lat: float, lon: float) -> dict:
"""κ·œμ œκ΅¬μ—­ 10μ’… 포함 μ—¬λΆ€ λ°˜ν™˜. True = 규제 적용."""
bbox = _box_filter(lat, lon)
def hits(layer_id: str) -> bool:
return len(_get(layer_id, bbox, size=1)) > 0
return {
"agri_promotion": hits("LT_C_AGRIXUE101"),
"agri_unfavorable": hits("LT_C_AGRIXUE102"),
"natural_conservation": hits("LT_C_UQ114"),
"wetland_protection": hits("LT_C_UM901"),
"forest_protection": hits("LT_C_UF151"),
"greenbelt": hits("LT_C_UD801"),
"water_source": hits("LT_C_UM710"),
"wildlife_protection": hits("LT_C_UM221"),
"steep_slope_hazard": hits("LT_C_UP401"),
"disaster_risk": hits("LT_C_UP201"),
}