"""카카오 지도 JS 키 · 좌표 → 주소 공용 길(529-2 · 591-1) — B08 거리이정표 · D 예정노선이 씀. GET /api/map/kakao-key {key} — 공개용(도메인 등록으로 막힘) · .env KAKAO_JS_KEY · 없으면 null GET /api/map/address {address, region, source} — VWorld(B08 과 같은 길) · 지번 먼저 · 없음 null GET /api/map/search?q= {items: [{kind address|place, title, address, lon, lat}], source} — 594-4 · 등록(D01) · 예정노선 위치 칸이 같이 씀 · VWorld 주소 + 장소 """ from __future__ import annotations import asyncio import os from typing import Any from fastapi import APIRouter, Query from B01_Dashboard.B01_Dashboard_Map import reverse_place, search_address, search_place router = APIRouter(prefix="/api/map", tags=["map"]) KAKAO_JS = "KAKAO_JS_KEY" ADDRESS_SOURCE = "VWorld" @router.get("/kakao-key") async def kakao_map_key() -> dict[str, Any]: """없으면 null = 화면 안내 한 줄.""" return {"key": os.environ.get(KAKAO_JS, "").strip() or None} @router.get("/address") async def map_address( lon: float = Query(..., ge=120, le=135), lat: float = Query(..., ge=30, le=45) ) -> dict[str, Any]: """좌표 → 주소 · 「시군구 읍면」(사업 지역 채움) · 출처.""" return {**await reverse_place(lon, lat), "source": ADDRESS_SOURCE} @router.get("/search") async def map_search( q: str = Query(..., min_length=1, max_length=100), size: int = Query(10, ge=1, le=30) ) -> dict[str, Any]: """주소 · 장소 찾기(594-4) — 주소(도로명 → 지번) 먼저 · 장소 뒤 · 같은 줄 하나 · 키는 서버.""" query = q.strip() if not query: return {"items": [], "source": ADDRESS_SOURCE} addresses, places = await asyncio.gather(search_address(query, size), search_place(query, size)) items = [ { "kind": "address", "title": hit[hit["category"]] or hit["road"] or hit["parcel"], "address": hit["parcel"] if hit["category"] == "road" else hit["road"], "lon": hit["lon"], "lat": hit["lat"], } for hit in addresses ] + [ { "kind": "place", "title": hit["title"], "address": hit["road"] or hit["parcel"], "lon": hit["lon"], "lat": hit["lat"], } for hit in places ] unique = {(item["title"], item["address"]): item for item in items} # 같은 줄 하나(차례 그대로) return {"items": list(unique.values()), "source": ADDRESS_SOURCE}