Spaces:
Sleeping
Sleeping
| """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=["*"], | |
| ) | |
| def health(): | |
| return {"status": "ok"} | |
| 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 | |
| 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)) | |
| 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}") | |