solarfit-api / api /main.py
LIM
perf: reduce MAX_BASE_TILES=8, mini-batch inference
0939b04
Raw
History Blame Contribute Delete
3.52 kB
"""SolarFit API μ„œλ²„."""
from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
import httpx
from api.analysis import analyze
from api.models import AnalysisResult, ScanRequest, ScanResult
from api.config import VWORLD_API_KEY, VWORLD_SEARCH_URL
from api.scanner import scan_region
app = FastAPI(title="SolarFit API", version="0.1.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173", "http://localhost:5174", "http://localhost:3000", "https://solarfit-beta.vercel.app", "https://limtaek-solarfit-api.hf.space"],
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/api/health")
def health():
return {"status": "ok"}
@app.get("/api/analyze", response_model=AnalysisResult)
def analyze_location(
lat: float = Query(..., description="μœ„λ„ (WGS84)", ge=33.0, le=39.0),
lon: float = Query(..., description="경도 (WGS84)", ge=124.0, le=132.0),
):
"""μ’Œν‘œ β†’ νƒœμ–‘κ΄‘ μž…μ§€ 뢄석 κ²°κ³Ό."""
try:
return analyze(lat=lat, lon=lon)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
def _vworld_search(q: str, search_type: str, category: str | None = None) -> list[dict]:
"""VWorld 단일 νƒ€μž… 검색 β€” κ²°κ³Ό μ—†μœΌλ©΄ []."""
base = {
"service": "search",
"request": "search",
"key": VWORLD_API_KEY,
"query": q,
"type": search_type,
"format": "json",
"size": "5",
"crs": "EPSG:4326",
}
if category:
base["category"] = category
r = httpx.get(VWORLD_SEARCH_URL, params=base, timeout=10)
r.raise_for_status()
body = r.json()
if body.get("response", {}).get("status") != "OK":
return []
items = body.get("response", {}).get("result", {}).get("items", [])
results = []
for item in items:
addr = item.get("address", {})
label = addr.get("road") or addr.get("parcel") or ""
results.append({
"title": item.get("title") or label,
"address": label,
"lat": float(item.get("point", {}).get("y", 0)),
"lon": float(item.get("point", {}).get("x", 0)),
})
return results
@app.post("/api/scan", response_model=ScanResult)
def scan(req: ScanRequest):
try:
return scan_region(req.lat_min, req.lat_max, req.lon_min, req.lon_max, req.threshold)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/search")
def search_address(q: str = Query(..., description="μ£Όμ†Œ λ˜λŠ” μž₯μ†Œλͺ…")):
"""VWorld 검색 API β€” μž₯μ†Œλͺ…Β·λ„λ‘œλͺ… μ£Όμ†Œ β†’ μ’Œν‘œ."""
if not VWORLD_API_KEY:
return {"results": []}
try:
# μž₯μ†Œλͺ…(POI) 검색 β€” category λΆˆν•„μš”
place_results = _vworld_search(q, "PLACE")
# λ„λ‘œλͺ… μ£Όμ†Œ 검색 β€” category=ROAD ν•„μˆ˜
road_results = _vworld_search(q, "ADDRESS", "ROAD")
seen: set[tuple] = set()
merged = []
for item in place_results + road_results:
key = (round(item["lat"], 5), round(item["lon"], 5))
if key not in seen:
seen.add(key)
merged.append(item)
if len(merged) >= 5:
break
return {"results": merged}
except (httpx.TimeoutException, httpx.RequestError, httpx.HTTPStatusError, ValueError, KeyError) as e:
raise HTTPException(status_code=502, detail=f"VWorld search error: {e}")