Spaces:
Sleeping
Sleeping
| import os | |
| import gradio as gr | |
| import geopandas as gpd | |
| import folium | |
| import requests | |
| # osmnx opsiyonel (fallback için), yoksa sorun değil | |
| try: | |
| import osmnx as ox | |
| except ImportError: | |
| ox = None | |
| from huggingface_hub import InferenceClient, login | |
| # ========================================== | |
| # HF TOKEN & MODEL | |
| # ========================================== | |
| HF_TOKEN = (os.environ.get("HUGGINGFACE_HUB_TOKEN", "") or "").strip() | |
| if HF_TOKEN: | |
| login(token=HF_TOKEN) | |
| else: | |
| print("UYARI: HF_TOKEN / HUGGINGFACE_HUB_TOKEN bulunamadı, gated modellere erişilemeyebilir.") | |
| #client = InferenceClient( | |
| # model="abacusai/Dracarys-72B-Instruct", | |
| # token=HF_TOKEN if HF_TOKEN else None, | |
| #) | |
| # ========================================== | |
| # ÖNCEDEN HAZIRLANMIŞ OSM VERİSİ | |
| # ========================================== | |
| NEIGH_PATH = "data/neighborhoods.geojson" | |
| POIS_PATH = "data/pois.geojson" | |
| if not (os.path.exists(NEIGH_PATH) and os.path.exists(POIS_PATH)): | |
| print("UYARI: data/ klasöründe neighborhoods.geojson / pois.geojson bulunamadı!") | |
| neighborhoods_gdf = None | |
| pois_gdf = None | |
| else: | |
| neighborhoods_gdf = gpd.read_file(NEIGH_PATH) | |
| pois_gdf = gpd.read_file(POIS_PATH) | |
| print(f"{len(neighborhoods_gdf)} mahalle/semt, {len(pois_gdf)} POI yüklendi.") | |
| DEFAULT_TAGS = { | |
| "amenity": ["school", "pharmacy", "hospital", "restaurant", "cafe", "bank"], | |
| "leisure": ["park", "playground"], | |
| "shop": True, | |
| # >>> ULAŞIM ETİKETLERİ | |
| "highway": ["bus_stop"], # otobüs durakları | |
| "railway": ["station", "halt", "tram_stop"], # tren / metro / tramvay istasyonları | |
| "public_transport": ["stop_position", "platform"], # toplu taşıma durak/istasyonları | |
| } | |
| # ========================================== | |
| # OSM / CBS FONKSİYONLARI | |
| # ========================================== | |
| from shapely.geometry import Point | |
| import math | |
| def get_neighborhood_center(city, district, neighborhood): | |
| """ | |
| Verilen şehir/ilçe/mahalle için poligonu alır ve merkez (centroid) koordinatını döndürür. | |
| (lat, lon) şeklinde WGS84 (EPSG:4326) döner. | |
| """ | |
| gdf = get_neighborhood_gdf(city, district, neighborhood) | |
| if gdf is None or len(gdf) == 0: | |
| return None | |
| # Emin olmak için WGS84'e çevir | |
| try: | |
| gdf_ll = gdf.to_crs(epsg=4326) | |
| except Exception: | |
| gdf_ll = gdf # zaten 4326 ise | |
| poly = gdf_ll.geometry.iloc[0] | |
| c = poly.centroid | |
| return c.y, c.x # (lat, lon) | |
| def _build_nearest_poi_query(lat, lon, key, values, radius): | |
| """ | |
| Overpass için: verilen key + values (örn: amenity=university, shop=supermarket) | |
| etrafında en yakın POI'yi arayan sorguyu üretir. | |
| """ | |
| if isinstance(values, str): | |
| values = [values] | |
| filters = [] | |
| for v in values: | |
| filters.append(f' node["{key}"="{v}"](around:{radius},{lat},{lon});') | |
| filters.append(f' way["{key}"="{v}"](around:{radius},{lat},{lon});') | |
| filters.append(f' rel["{key}"="{v}"](around:{radius},{lat},{lon});') | |
| filters_str = "\n".join(filters) | |
| query = f""" | |
| [out:json][timeout:25]; | |
| ( | |
| {filters_str} | |
| ); | |
| out center 1; | |
| """ | |
| return query | |
| def _haversine_m(lat1, lon1, lat2, lon2): | |
| """ | |
| İki nokta arasındaki yaklaşık mesafeyi (metre) hesaplar. | |
| """ | |
| R = 6371000 # Dünya yarıçapı (m) | |
| phi1 = math.radians(lat1) | |
| phi2 = math.radians(lat2) | |
| dphi = math.radians(lat2 - lat1) | |
| dlambda = math.radians(lon2 - lon1) | |
| a = math.sin(dphi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(dlambda/2)**2 | |
| c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) | |
| return R * c | |
| def find_nearest_poi_overpass(city, district, neighborhood, key, values, radius=5000): | |
| """ | |
| Mahalle merkezinden belirli bir yarıçap içinde (m cinsinden) | |
| verilen OSM tag'ine göre (örn: amenity, shop) en yakın POI'yi arar. | |
| Örnek: | |
| key="amenity", values=["university", "college"] | |
| key="shop", values=["supermarket", "convenience"] | |
| Dönüş: | |
| None -> bulunamadı | |
| dict -> {"name", "lat", "lon", "tag_key", "tag_value", "distance_m", "distance_km"} | |
| """ | |
| center = get_neighborhood_center(city, district, neighborhood) | |
| if center is None: | |
| return None | |
| lat_c, lon_c = center | |
| query = _build_nearest_poi_query(lat_c, lon_c, key, values, radius) | |
| url = "https://overpass-api.de/api/interpreter" | |
| try: | |
| resp = requests.post(url, data={"data": query}, timeout=30) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| except Exception as e: | |
| print("Overpass nearest POI isteği hatası:", e) | |
| return None | |
| elements = data.get("elements", []) | |
| if not elements: | |
| return None | |
| # İlk element en yakın kabul ediliyor | |
| e = elements[0] | |
| tags = e.get("tags", {}) or {} | |
| name = tags.get("name", "(isimsiz POI)") | |
| # node ise lat/lon direk var; way/rel ise center altında | |
| lat_p = e.get("lat") or (e.get("center") or {}).get("lat") | |
| lon_p = e.get("lon") or (e.get("center") or {}).get("lon") | |
| tag_value = None | |
| if isinstance(values, list): | |
| for v in values: | |
| if tags.get(key) == v: | |
| tag_value = v | |
| break | |
| if tag_value is None: | |
| tag_value = tags.get(key) | |
| else: | |
| tag_value = tags.get(key) | |
| dist_m = None | |
| dist_km = None | |
| if lat_p is not None and lon_p is not None: | |
| dist_m = _haversine_m(lat_c, lon_c, lat_p, lon_p) | |
| dist_km = dist_m / 1000.0 | |
| return { | |
| "name": name, | |
| "lat": lat_p, | |
| "lon": lon_p, | |
| "tag_key": key, | |
| "tag_value": tag_value, | |
| "distance_m": dist_m, | |
| "distance_km": dist_km, | |
| } | |
| def answer_nearest_poi(city, district, neighborhood, key, values, human_label=None, radius=5000): | |
| """ | |
| Kullanıcıya gösterilecek generic cevap metni. | |
| human_label: 'üniversite', 'market', 'hastane' gibi daha okunaklı isim. | |
| """ | |
| info = find_nearest_poi_overpass(city, district, neighborhood, key, values, radius=radius) | |
| if info is None: | |
| label = human_label or f"{key}={values}" | |
| return f"{city} / {district} / {neighborhood} çevresinde belirli bir yarıçapta {label} bulunamadı." | |
| name = info["name"] | |
| lat = info["lat"] | |
| lon = info["lon"] | |
| dist_km = info["distance_km"] | |
| label = human_label or info.get("tag_value") or f"{key}={values}" | |
| if lat is not None and lon is not None and dist_km is not None: | |
| return ( | |
| f"{city} / {district} / {neighborhood} mahallesi merkezine en yakın {label}: " | |
| f"{name} (yaklaşık {dist_km:.2f} km, konum: {lat:.5f}, {lon:.5f})." | |
| ) | |
| else: | |
| return ( | |
| f"{city} / {district} / {neighborhood} mahallesi merkezine en yakın {label}: {name}." | |
| ) | |
| def nearest_poi_wrapper(city, district, neighborhood, poi_type): | |
| """ | |
| Gradio dropdown'dan seçilen poi_type'ı gerçek tag'lere çeviren yardımcı. | |
| """ | |
| if poi_type == "Üniversite": | |
| key = "amenity" | |
| values = ["university", "college"] | |
| label = "üniversite" | |
| elif poi_type == "Market": | |
| key = "shop" | |
| values = ["supermarket", "convenience", "mall", "department_store"] | |
| label = "market" | |
| elif poi_type == "Hastane": | |
| key = "amenity" | |
| values = "hospital" | |
| label = "hastane" | |
| elif poi_type == "Eczane": | |
| key = "amenity" | |
| values = "pharmacy" | |
| label = "eczane" | |
| elif poi_type == "Park": | |
| key = "leisure" | |
| values = "park" | |
| label = "park" | |
| else: | |
| # varsayılan: üniversite | |
| key = "amenity" | |
| values = ["university", "college"] | |
| label = "üniversite" | |
| return answer_nearest_poi(city, district, neighborhood, key, values, human_label=label) | |
| def get_neighborhood_gdf(city: str, district: str, neighborhood: str): | |
| """ | |
| 1) Önce önceden kaydedilmiş GeoJSON'dan arar (city + district + neighborhood). | |
| 2) Eğer orada yoksa ve osmnx mevcutsa, OSM'den canlı çeker (fallback). | |
| """ | |
| city = (city or "").strip() | |
| district = (district or "").strip() | |
| neighborhood = (neighborhood or "").strip() | |
| # 1) Precomputed veri | |
| if neighborhoods_gdf is not None: | |
| mask = ( | |
| (neighborhoods_gdf["city"] == city) | |
| & (neighborhoods_gdf["district"] == district) | |
| & (neighborhoods_gdf["neighborhood"] == neighborhood) | |
| ) | |
| gdf_local = neighborhoods_gdf[mask] | |
| if gdf_local is not None and len(gdf_local) > 0: | |
| return gdf_local | |
| # 2) Fallback: canlı OSM çağrısı | |
| if ox is None: | |
| print("OSM fallback kullanılamıyor: osmnx yüklü değil.") | |
| return None | |
| # İlçe bilgisini de geocode sorgusuna ekliyoruz | |
| query = f"{neighborhood}, {district}, {city}, Türkiye" | |
| print(f"OSM fallback: {query}") | |
| try: | |
| gdf_osm = ox.geocode_to_gdf(query) | |
| except Exception as e: | |
| print("Mahalle geocode hatası (OSM fallback):", e) | |
| return None | |
| if gdf_osm is None or len(gdf_osm) == 0: | |
| return None | |
| gdf_osm = gdf_osm.copy() | |
| gdf_osm["city"] = city | |
| gdf_osm["district"] = district | |
| gdf_osm["neighborhood"] = neighborhood | |
| return gdf_osm | |
| def get_pois_within(gdf, tags=None): | |
| """ | |
| 1) Eğer GeoJSON'da bu mahalle için POI varsa, oradan döner. | |
| 2) Yoksa ve osmnx mevcutsa, poligon üzerinden OSM'den canlı çeker (fallback). | |
| """ | |
| if gdf is None: | |
| return None | |
| if tags is None: | |
| tags = DEFAULT_TAGS | |
| # 1) Precomputed POI verisi | |
| if ( | |
| pois_gdf is not None | |
| and all(col in gdf.columns for col in ["city", "district", "neighborhood"]) | |
| ): | |
| row = gdf.iloc[0] | |
| city = row["city"] | |
| district = row["district"] | |
| neighborhood = row["neighborhood"] | |
| mask = ( | |
| (pois_gdf["city"] == city) | |
| & (pois_gdf["district"] == district) | |
| & (pois_gdf["neighborhood"] == neighborhood) | |
| ) | |
| pois_local = pois_gdf[mask] | |
| if pois_local is not None and len(pois_local) > 0: | |
| return pois_local | |
| # 2) Fallback: OSM'den canlı POI çek | |
| if ox is None: | |
| print("OSM POI fallback kullanılamıyor: osmnx yüklü değil.") | |
| return None | |
| try: | |
| polygon = gdf.geometry.iloc[0] | |
| pois_osm = ox.features_from_polygon(polygon, tags) | |
| return pois_osm | |
| except Exception as e: | |
| print("POI hatası (OSM fallback):", e) | |
| return None | |
| def summarize_pois(gdf, pois): | |
| summary = {} | |
| try: | |
| area_m2 = gdf.to_crs(epsg=32636).geometry.iloc[0].area | |
| summary["alan_m2"] = float(area_m2) | |
| summary["alan_km2"] = float(area_m2 / 1_000_000) | |
| except Exception as e: | |
| print("Alan hesaplama hatası:", e) | |
| summary["alan_m2"] = None | |
| summary["alan_km2"] = None | |
| if pois is None or len(pois) == 0: | |
| summary["toplam_poi"] = 0 | |
| return summary | |
| summary["toplam_poi"] = int(len(pois)) | |
| if "amenity" in pois.columns: | |
| amenity_counts = pois["amenity"].value_counts().to_dict() | |
| for k, v in amenity_counts.items(): | |
| summary[f"amenity_{k}"] = int(v) | |
| if "leisure" in pois.columns: | |
| leisure_counts = pois["leisure"].value_counts().to_dict() | |
| for k, v in leisure_counts.items(): | |
| summary[f"leisure_{k}"] = int(v) | |
| if "shop" in pois.columns: | |
| shop_counts = pois["shop"].value_counts().to_dict() | |
| for k, v in shop_counts.items(): | |
| summary[f"shop_{k}"] = int(v) | |
| # >>> ULAŞIM: highway / railway / public_transport | |
| if "highway" in pois.columns: | |
| hw_counts = pois["highway"].value_counts().to_dict() | |
| for k, v in hw_counts.items(): | |
| summary[f"highway_{k}"] = int(v) | |
| if "railway" in pois.columns: | |
| rw_counts = pois["railway"].value_counts().to_dict() | |
| for k, v in rw_counts.items(): | |
| summary[f"railway_{k}"] = int(v) | |
| if "public_transport" in pois.columns: | |
| pt_counts = pois["public_transport"].value_counts().to_dict() | |
| for k, v in pt_counts.items(): | |
| summary[f"public_transport_{k}"] = int(v) | |
| return summary | |
| def build_poi_names_text(pois, max_per_category=15) -> str: | |
| """ | |
| POI GeoDataFrame'inden okul, eczane, kafe, restoran, market vb. | |
| için isim listeleri çıkarır. LLM bağlamında kullanılacak metni döndürür. | |
| """ | |
| if pois is None or len(pois) == 0: | |
| return "Bu mahalle için isim verisi olan POI bulunamadı.\n" | |
| if "name" not in pois.columns: | |
| return "Bu mahallede POI'ler için 'name' alanı bulunamadı.\n" | |
| lines = [] | |
| def add_category(title, mask): | |
| sub = pois[mask] | |
| if sub is None or len(sub) == 0: | |
| return | |
| names = ( | |
| sub["name"] | |
| .dropna() | |
| .astype(str) | |
| .str.strip() | |
| ) | |
| names = [n for n in names if n] | |
| if not names: | |
| return | |
| unique_names = sorted(set(names))[:max_per_category] | |
| lines.append(f"{title}:") | |
| for n in unique_names: | |
| lines.append(f" - {n}") | |
| lines.append("") # kategori arası boş satır | |
| # Kategoriler | |
| if "amenity" in pois.columns: | |
| amenity = pois["amenity"] | |
| add_category("Okullar", amenity == "school") | |
| add_category("Eczaneler", amenity == "pharmacy") | |
| add_category("Kafeler", amenity == "cafe") | |
| add_category("Restoranlar", amenity == "restaurant") | |
| if "shop" in pois.columns: | |
| shop = pois["shop"] | |
| # İstersen buraya başka shop türleri de ekleyebilirsin | |
| add_category("Marketler", shop.isin(["supermarket", "convenience", "mall", "department_store"])) | |
| if not lines: | |
| return "Bu mahallede adı bilinen POI listesi çıkarılamadı.\n" | |
| return "\n".join(lines) | |
| def build_stats_text(summary: dict) -> str: | |
| if not summary: | |
| return "Veri bulunamadı." | |
| alan = summary.get("alan_km2", 0) or 0.0 | |
| toplam_poi = summary.get("toplam_poi", 0) | |
| okul = summary.get("amenity_school", 0) | |
| park = summary.get("leisure_park", 0) | |
| eczane = summary.get("amenity_pharmacy", 0) | |
| cafe = summary.get("amenity_cafe", 0) | |
| restoran = summary.get("amenity_restaurant", 0) | |
| # >>> ULAŞIM SAYILARI | |
| otobus_duragi = summary.get("highway_bus_stop", 0) | |
| tren_istasyonu = summary.get("railway_station", 0) + summary.get("railway_halt", 0) | |
| tramvay_duragi = summary.get("railway_tram_stop", 0) | |
| pt_platform = summary.get("public_transport_platform", 0) | |
| lines = [ | |
| f"- Tahmini alan: {alan:.2f} km²", | |
| f"- Toplam POI (ilgi noktası): {toplam_poi}", | |
| f"- Okul sayısı: {okul}", | |
| f"- Park sayısı: {park}", | |
| f"- Eczane sayısı: {eczane}", | |
| f"- Kafe sayısı: {cafe}", | |
| f"- Restoran sayısı: {restoran}", | |
| # >>> ULAŞIM SATIRLARI | |
| f"- Otobüs durağı sayısı: {otobus_duragi}", | |
| f"- Tren/metro istasyonu sayısı: {tren_istasyonu}", | |
| f"- Tramvay durağı sayısı: {tramvay_duragi}", | |
| f"- Toplu taşıma platform/istasyon öğesi: {pt_platform}", | |
| ] | |
| return "\n".join(lines) | |
| import requests | |
| import json | |
| import geopandas as gpd | |
| from shapely.geometry import shape | |
| from pathlib import Path | |
| def is_osm_query(message: str) -> bool: | |
| return isinstance(message, str) and message.strip().lower().startswith("/osm") | |
| OSM_PROMPT_PATH = Path("osm_query_system.txt") # veya Path("data/osm_query_system.txt") | |
| try: | |
| with OSM_PROMPT_PATH.open("r", encoding="utf-8") as f: | |
| OSM_QUERY_SYSTEM = f.read() | |
| print(f"OSM system prompt '{OSM_PROMPT_PATH}' dosyasından yüklendi.") | |
| except FileNotFoundError: | |
| print(f"UYARI: {OSM_PROMPT_PATH} bulunamadı, kısa yedek prompt kullanılacak.") | |
| OSM_QUERY_SYSTEM = """ | |
| You are an expert in OpenStreetMap and Overpass API. | |
| Produce only valid Overpass QL. | |
| No explanations. No markdown. | |
| Use [out:json][timeout:25]; at top. | |
| End with: out center; | |
| """ | |
| def generate_overpass_query_from_llm(prompt: str, model_name: str) -> str: | |
| """ | |
| Doğal dilde verilen prompt'u kullanarak Overpass QL sorgusu üretir. | |
| Sadece geçerli Overpass QL döndürmeye çalışır, markdown vs. temizler. | |
| """ | |
| prompt = (prompt or "").strip() | |
| if not prompt: | |
| prompt = "Generate an Overpass QL query for my request." | |
| client = InferenceClient(model=model_name, token=HF_TOKEN if HF_TOKEN else None) | |
| messages = [ | |
| {"role": "system", "content": OSM_QUERY_SYSTEM}, | |
| {"role": "user", "content": prompt}, | |
| ] | |
| # Streaming'e gerek yok, tek seferde alalım | |
| result = client.chat_completion( | |
| messages=messages, | |
| max_tokens=400, | |
| temperature=0.2, | |
| top_p=0.9, | |
| stream=False, | |
| ) | |
| # HF InferenceClient sonucu | |
| query_text = result.choices[0].message.content | |
| # Olası ```ql ``` bloklarını temizle | |
| query_text = query_text.replace("```ql", "").replace("```QL", "").replace("```", "") | |
| return query_text.strip() | |
| def normalize_overpass_query(raw_query: str) -> str: | |
| if not raw_query: | |
| return "" | |
| q = raw_query.strip() | |
| if "{{geocodeArea:" in q: | |
| # Kullanıcıyı uyarmak için | |
| raise ValueError("Overpass API, {{geocodeArea:...}} makrosunu desteklemez. " | |
| "Lütfen bunun yerine area[...] filtresi veya numeric area id kullanın.") | |
| # (senin mevcut temizleme kodların) | |
| for token in ("```ql", "```QL", "```", "<code>", "</code>", "<pre>", "</pre>"): | |
| q = q.replace(token, "") | |
| return q.strip() | |
| # ========================================== | |
| # MAHALLE KARŞILAŞTIRMA BAĞLAMI | |
| # ========================================== | |
| def prepare_comparison(city1, district1, neigh1,city2, district2, neigh2): | |
| """ | |
| Butona basıldığında: | |
| - Her iki mahalle için OSM özetini hazırlar | |
| - İki metin döndürür | |
| - LLM için karşılaştırma bağlamını üretir | |
| """ | |
| city1 = (city1 or "").strip() | |
| district1 = (district1 or "").strip() | |
| neigh1 = (neigh1 or "").strip() | |
| city2 = (city2 or "").strip() | |
| district2 = (district2 or "").strip() | |
| neigh2 = (neigh2 or "").strip() | |
| if not city1 or not district1 or not city1 or not neigh1 or not district2 or not neigh2: | |
| msg = "Şehir, ilçe ve iki mahalle de girilmelidir." | |
| return (msg, msg, "", "<b>Harita için yeterli veri yok.</b>") | |
| # Varsayılan olarak boş metinler | |
| labels1 = "Bu mahalle için isim verisi çıkarılamadı." | |
| labels2 = "Bu mahalle için isim verisi çıkarılamadı." | |
| # Mahalle 1 | |
| gdf1 = get_neighborhood_gdf(city1, district1, neigh1) | |
| if gdf1 is None or len(gdf1) == 0: | |
| stats1 = f"{city1} / {district1} / {neigh1} için veri bulunamadı." | |
| summary1 = None | |
| else: | |
| pois1 = get_pois_within(gdf1) | |
| summary1 = summarize_pois(gdf1, pois1) | |
| stats1 = build_stats_text(summary1) | |
| # >>> POI isim metni | |
| labels1 = build_poi_names_text(pois1) | |
| # Mahalle 2 | |
| gdf2 = get_neighborhood_gdf(city2, district2, neigh2) | |
| if gdf2 is None or len(gdf2) == 0: | |
| stats2 = f"{city2} / {district2} / {neigh2} için veri bulunamadı." | |
| summary2 = None | |
| else: | |
| pois2 = get_pois_within(gdf2) | |
| summary2 = summarize_pois(gdf2, pois2) | |
| stats2 = build_stats_text(summary2) | |
| # >>> POI isim metni | |
| labels2 = build_poi_names_text(pois2) | |
| # LLM bağlamı | |
| compare_context_parts = [ | |
| f"Şehir: {city1}", | |
| "", | |
| f"1. Mahalle: {neigh1} (İlçe: {district1})", | |
| stats1, | |
| "", | |
| "1. mahalledeki önemli POI isimleri (okullar, eczaneler, marketler, kafeler vb.):", | |
| labels1, | |
| "", | |
| f"Şehir: {city2}", | |
| "", | |
| f"2. Mahalle: {neigh2} (İlçe: {district2})", | |
| stats2, | |
| "", | |
| "2. mahalledeki önemli POI isimleri (okullar, eczaneler, marketler, kafeler vb.):", | |
| labels2, | |
| "", | |
| "Bu iki mahalleyi alan, toplam POI sayısı, park, okul, kafe, restoran, eczane sayıları" | |
| " ve verilen POI isimleri açısından karşılaştır." | |
| " Kullanıcı soru sorarsa, hem sayısal verilere hem de POI isimlerine dayanarak" | |
| " açıklayıcı ve dengeli bir karşılaştırma yap." | |
| ] | |
| compare_context = "\n".join(compare_context_parts) | |
| # Harita HTML'i | |
| map_html = create_comparison_map(gdf1, gdf2) | |
| return stats1, stats2, compare_context, map_html,gdf1, gdf2 | |
| def add_poi_markers_to_map(pois, m, layer_prefix="POI"): | |
| """ | |
| POI GeoDataFrame'ini alır, amenity/leisure/shop/railway/highway/public_transport | |
| sütunlarına göre kategorik katmanlar oluşturup haritaya ekler. | |
| """ | |
| if pois is None or len(pois) == 0: | |
| return | |
| # Gerekirse WGS84'e (lat/lon) çevir | |
| try: | |
| if pois.crs is not None and pois.crs.to_epsg() != 4326: | |
| pois = pois.to_crs(epsg=4326) | |
| except Exception: | |
| # CRS yoksa veya hata olursa direkt devam | |
| pass | |
| # >>> ÖNEMLİ: layer_groups burada tanımlanmalı | |
| layer_groups = {} # {kategori_ismi: folium.FeatureGroup} | |
| for _, row in pois.iterrows(): | |
| geom = row.geometry | |
| if geom is None or geom.is_empty: | |
| continue | |
| # Nokta değilse centroid al | |
| try: | |
| if geom.geom_type == "Point": | |
| lat, lon = geom.y, geom.x | |
| else: | |
| c = geom.centroid | |
| lat, lon = c.y, c.x | |
| except Exception: | |
| continue | |
| amenity = row.get("amenity") | |
| leisure = row.get("leisure") | |
| shop = row.get("shop") | |
| highway = row.get("highway") | |
| railway = row.get("railway") | |
| public_transport = row.get("public_transport") | |
| # Kategori belirle (öncelik: amenity > leisure > shop > railway > highway > public_transport) | |
| if isinstance(amenity, str): | |
| cat = f"Amenity: {amenity}" | |
| elif isinstance(leisure, str): | |
| cat = f"Leisure: {leisure}" | |
| elif isinstance(shop, str): | |
| cat = f"Shop: {shop}" | |
| elif isinstance(railway, str): | |
| cat = f"Railway: {railway}" | |
| elif isinstance(highway, str): | |
| cat = f"Highway: {highway}" | |
| elif isinstance(public_transport, str): | |
| cat = f"PT: {public_transport}" | |
| else: | |
| cat = "Diğer" | |
| layer_name = f"{layer_prefix} - {cat}" | |
| if layer_name not in layer_groups: | |
| fg = folium.FeatureGroup(name=layer_name, show=True) | |
| fg.add_to(m) | |
| layer_groups[layer_name] = fg | |
| name = row.get("name") | |
| popup_items = [] | |
| # Önce isim | |
| if isinstance(name, str) and name.strip(): | |
| popup_items.append(name.strip()) | |
| if isinstance(amenity, str): | |
| popup_items.append(f"amenity={amenity}") | |
| if isinstance(leisure, str): | |
| popup_items.append(f"leisure={leisure}") | |
| if isinstance(shop, str): | |
| popup_items.append(f"shop={shop}") | |
| if isinstance(railway, str): | |
| popup_items.append(f"railway={railway}") | |
| if isinstance(highway, str): | |
| popup_items.append(f"highway={highway}") | |
| if isinstance(public_transport, str): | |
| popup_items.append(f"public_transport={public_transport}") | |
| popup_text = ", ".join(popup_items) if popup_items else "POI" | |
| folium.CircleMarker( | |
| location=[lat, lon], | |
| radius=4, | |
| popup=popup_text, | |
| tooltip=name.strip() if isinstance(name, str) and name.strip() else None, | |
| weight=1, | |
| fill=True, | |
| fill_opacity=0.7, | |
| ).add_to(layer_groups[layer_name]) | |
| def create_comparison_map(gdf1, gdf2): | |
| """ | |
| İki mahalle poligonunu tek bir Folium haritasında gösterir. | |
| + Her mahalle için POI'leri kategorik katmanlar hâlinde ekler. | |
| """ | |
| # Hiç veri yoksa | |
| if (gdf1 is None or len(gdf1) == 0) and (gdf2 is None or len(gdf2) == 0): | |
| return "<b>Harita için yeterli veri yok.</b>" | |
| # Merkez olarak mevcut bir poligonun centroid'ini al | |
| centroid = None | |
| if gdf1 is not None and len(gdf1) > 0: | |
| centroid = gdf1.geometry.iloc[0].centroid | |
| elif gdf2 is not None and len(gdf2) > 0: | |
| centroid = gdf2.geometry.iloc[0].centroid | |
| if centroid is None: | |
| return "<b>Geometri bulunamadı.</b>" | |
| m = folium.Map(location=[centroid.y, centroid.x], zoom_start=14) | |
| # Mahalle 1 | |
| pois1 = None | |
| if gdf1 is not None and len(gdf1) > 0: | |
| name1 = str(gdf1.get("neighborhood", ["Mahalle 1"]).iloc[0]) | |
| folium.GeoJson( | |
| gdf1.geometry.__geo_interface__, | |
| name=name1, | |
| style_function=lambda feat: { | |
| "color": "red", | |
| "fill": False, | |
| "weight": 3, | |
| }, | |
| ).add_to(m) | |
| # >>> Mahalle 1 için POI'ler | |
| pois1 = get_pois_within(gdf1) | |
| add_poi_markers_to_map(pois1, m, layer_prefix=f"{name1} POI") | |
| # Mahalle 2 | |
| pois2 = None | |
| if gdf2 is not None and len(gdf2) > 0: | |
| name2 = str(gdf2.get("neighborhood", ["Mahalle 2"]).iloc[0]) | |
| folium.GeoJson( | |
| gdf2.geometry.__geo_interface__, | |
| name=name2, | |
| style_function=lambda feat: { | |
| "color": "blue", | |
| "fill": False, | |
| "weight": 3, | |
| }, | |
| ).add_to(m) | |
| # >>> Mahalle 2 için POI'ler | |
| pois2 = get_pois_within(gdf2) | |
| add_poi_markers_to_map(pois2, m, layer_prefix=f"{name2} POI") | |
| folium.LayerControl().add_to(m) | |
| return m._repr_html_() | |
| def run_overpass_to_map(query: str, | |
| previous_elements: list | None, | |
| previous_spatial: str | None, | |
| layer_color: str, | |
| base_gdf1=None, | |
| base_gdf2=None): | |
| layer_name = "Spatial Query" | |
| query = normalize_overpass_query(query) if 'normalize_overpass_query' in globals() else query | |
| if not query or not query.strip(): | |
| return ( | |
| "<b>Overpass sorgusu boş.</b>", | |
| previous_spatial or "Geçerli bir Overpass sorgusu sağlanmadı.", | |
| previous_elements, | |
| ) | |
| url = "https://overpass-api.de/api/interpreter" | |
| try: | |
| resp = requests.post(url, data={"data": query}, timeout=30) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| except Exception as e: | |
| print("Overpass isteği hatası:", e) | |
| try: | |
| print("Overpass response text:", resp.text[:500]) | |
| except Exception: | |
| pass | |
| return ( | |
| f"<b>Overpass isteği hatası:</b> {e}", | |
| previous_spatial or "Overpass isteğinde hata oluştu, veri yok.", | |
| previous_elements, | |
| ) | |
| new_elements = data.get("elements", []) | |
| if not new_elements: | |
| return ( | |
| "<b>Overpass sonucu: veri bulunamadı.</b>", | |
| previous_spatial or "Overpass sonucu: hiç element bulunamadı.", | |
| previous_elements, | |
| ) | |
| # ---- Eski + yeni elementleri birleştir ---- | |
| if not previous_elements: | |
| all_elements = new_elements | |
| else: | |
| all_elements = previous_elements + new_elements | |
| # ---- Merkez bul (tüm elementler üzerinden) ---- | |
| center_lat, center_lon = None, None | |
| for el in all_elements: | |
| if "lat" in el and "lon" in el: | |
| center_lat, center_lon = el["lat"], el["lon"] | |
| break | |
| if center_lat is None: | |
| return ( | |
| "<b>Overpass sonucu: nokta verisi yok.</b>", | |
| previous_spatial or "Overpass sonucu: nokta verisi bulunamadı.", | |
| all_elements, | |
| ) | |
| # ---- Haritayı çiz ---- | |
| # Önce merkez: mümkünse mahallelerden, yoksa Overpass noktalardan | |
| if base_gdf1 is not None and len(base_gdf1) > 0: | |
| c = base_gdf1.geometry.iloc[0].centroid | |
| center_lat, center_lon = c.y, c.x | |
| elif base_gdf2 is not None and len(base_gdf2) > 0: | |
| c = base_gdf2.geometry.iloc[0].centroid | |
| center_lat, center_lon = c.y, c.x | |
| # (aksi halde yukarıda all_elements'tan zaten bulduk) | |
| m = folium.Map(location=[center_lat, center_lon], zoom_start=14) | |
| # 1. mahalleyi tekrar çiz | |
| if base_gdf1 is not None and len(base_gdf1) > 0: | |
| name1 = str(base_gdf1.get("neighborhood", ["Mahalle 1"]).iloc[0]) | |
| folium.GeoJson( | |
| base_gdf1.geometry.__geo_interface__, | |
| name=name1, | |
| style_function=lambda feat: { | |
| "color": "red", | |
| "fill": False, | |
| "weight": 3, | |
| }, | |
| ).add_to(m) | |
| pois1 = get_pois_within(base_gdf1) | |
| add_poi_markers_to_map(pois1, m, layer_prefix=f"{name1} POI") | |
| # 2. mahalleyi tekrar çiz | |
| if base_gdf2 is not None and len(base_gdf2) > 0: | |
| name2 = str(base_gdf2.get("neighborhood", ["Mahalle 2"]).iloc[0]) | |
| folium.GeoJson( | |
| base_gdf2.geometry.__geo_interface__, | |
| name=name2, | |
| style_function=lambda feat: { | |
| "color": "blue", | |
| "fill": False, | |
| "weight": 3, | |
| }, | |
| ).add_to(m) | |
| pois2 = get_pois_within(base_gdf2) | |
| add_poi_markers_to_map(pois2, m, layer_prefix=f"{name2} POI") | |
| # Burada tek bir "spatial" katman oluşturuyoruz | |
| fg_nodes = folium.FeatureGroup( | |
| name=f"{layer_name} - Noktalar", # katman adı | |
| show=True | |
| ) | |
| fg_nodes.add_to(m) | |
| fg_ways = folium.FeatureGroup( | |
| name=f"{layer_name} - Yollar", | |
| show=True | |
| ) | |
| fg_ways.add_to(m) | |
| for el in all_elements: | |
| etype = el.get("type") | |
| tags = el.get("tags", {}) or {} | |
| name = tags.get("name", "") | |
| popup_items = [] | |
| if name: | |
| popup_items.append(name) | |
| for k, v in tags.items(): | |
| if k != "name": | |
| popup_items.append(f"{k}={v}") | |
| popup_text = "<br>".join(popup_items) if popup_items else etype | |
| if etype == "node" and "lat" in el and "lon" in el: | |
| folium.CircleMarker( | |
| location=[el["lat"], el["lon"]], | |
| radius=4, | |
| popup=popup_text, | |
| tooltip=name or None, | |
| weight=1, | |
| fill=True, | |
| fill_opacity=0.7, | |
| color=layer_color, # 👈 stroke rengi | |
| fill_color=layer_color, # 👈 doldurma rengi | |
| ).add_to(fg_nodes) | |
| elif etype == "way" and "geometry" in el: | |
| coords = [(p["lat"], p["lon"]) for p in el["geometry"]] | |
| if len(coords) >= 2: | |
| folium.PolyLine( | |
| locations=coords, | |
| popup=popup_text, | |
| weight=3, | |
| color=layer_color, # 👈 yol rengi | |
| ).add_to(fg_ways) | |
| folium.LayerControl().add_to(m) | |
| map_html = m._repr_html_() | |
| # ---- Yeni spatial özet ---- | |
| new_summary = summarize_overpass_data({"elements": new_elements}) | |
| if previous_spatial: | |
| combined_spatial = previous_spatial + "\n\n--- Yeni Sorgu ---\n" + new_summary | |
| else: | |
| combined_spatial = new_summary | |
| return map_html, combined_spatial, all_elements | |
| def llm_overpass_to_map(natural_prompt: str, | |
| model_name: str, | |
| previous_elements: list | None, | |
| previous_spatial: str | None, | |
| layer_color: str, | |
| base_gdf1=None, | |
| base_gdf2=None): | |
| if not natural_prompt or not natural_prompt.strip(): | |
| return ( | |
| "Doğal dil sorgu boş.", | |
| "<b>Overpass sonucu: sorgu üretilemedi.</b>", | |
| previous_spatial or "Overpass sonucu: sorgu üretilemedi.", | |
| previous_elements, | |
| ) | |
| try: | |
| query = generate_overpass_query_from_llm(natural_prompt, model_name) | |
| except Exception as e: | |
| print("LLM Overpass üretim hatası:", e) | |
| return ( | |
| f"LLM Overpass üretim hatası: {e}", | |
| "<b>Overpass sonucu: LLM hatası.</b>", | |
| previous_spatial or "Overpass sonucu: LLM hatası.", | |
| previous_elements, | |
| ) | |
| map_html, combined_spatial, all_elements = run_overpass_to_map( | |
| query, | |
| previous_elements, | |
| previous_spatial, | |
| layer_color, | |
| base_gdf1, | |
| base_gdf2, | |
| ) | |
| # 4 output: üretilen query, harita, birikmiş spatial metin, birikmiş element listesi | |
| return query, map_html, combined_spatial, all_elements | |
| def summarize_overpass_data(data: dict, max_examples: int = 30) -> str: | |
| """ | |
| Overpass JSON sonucundan LLM'e verilecek metinsel bir özet üretir. | |
| Çok büyük verilerde token patlamaması için sınırlı örnek verir. | |
| """ | |
| if not data: | |
| return "Overpass sonucu boş veya geçersiz.\n" | |
| elements = data.get("elements", []) | |
| if not elements: | |
| return "Overpass sonucu: hiç element bulunamadı.\n" | |
| total_nodes = sum(1 for e in elements if e.get("type") == "node") | |
| total_ways = sum(1 for e in elements if e.get("type") == "way") | |
| total_rel = sum(1 for e in elements if e.get("type") == "relation") | |
| # Basit tag istatistikleri | |
| amenity_counts = {} | |
| leisure_counts = {} | |
| shop_counts = {} | |
| highway_counts = {} | |
| railway_counts = {} | |
| pt_counts = {} | |
| examples = [] | |
| for e in elements[:max_examples]: | |
| etype = e.get("type") | |
| tags = e.get("tags", {}) | |
| name = tags.get("name", "(isimsiz)") | |
| amenity = tags.get("amenity") | |
| leisure = tags.get("leisure") | |
| shop = tags.get("shop") | |
| highway = tags.get("highway") | |
| railway = tags.get("railway") | |
| pt = tags.get("public_transport") | |
| opening_hours = tags.get("opening_hours") | |
| wheelchair = tags.get("wheelchair") | |
| if amenity: | |
| amenity_counts[amenity] = amenity_counts.get(amenity, 0) + 1 | |
| if leisure: | |
| leisure_counts[leisure] = leisure_counts.get(leisure, 0) + 1 | |
| if shop: | |
| shop_counts[shop] = shop_counts.get(shop, 0) + 1 | |
| if highway: | |
| highway_counts[highway] = highway_counts.get(highway, 0) + 1 | |
| if railway: | |
| railway_counts[railway] = railway_counts.get(railway, 0) + 1 | |
| if pt: | |
| pt_counts[pt] = pt_counts.get(pt, 0) + 1 | |
| # Örnek satır | |
| tag_parts = [] | |
| for k in ["amenity", "leisure", "shop", "highway", "railway", "public_transport", | |
| "opening_hours", "wheelchair"]: | |
| v = tags.get(k) | |
| if v: | |
| tag_parts.append(f"{k}={v}") | |
| tag_text = ", ".join(tag_parts) if tag_parts else "etiket yok" | |
| examples.append(f"- {etype} | {name} | {tag_text}") | |
| def dict_to_lines(title, d): | |
| if not d: | |
| return [] | |
| items = sorted(d.items(), key=lambda x: -x[1]) | |
| lines = [title] | |
| for k, v in items: | |
| lines.append(f" - {k}: {v}") | |
| return lines | |
| lines = [ | |
| f"Toplam node sayısı: {total_nodes}", | |
| f"Toplam way sayısı: {total_ways}", | |
| f"Toplam relation sayısı: {total_rel}", | |
| "", | |
| ] | |
| lines += dict_to_lines("Amenity türleri:", amenity_counts) | |
| lines += dict_to_lines("Leisure türleri:", leisure_counts) | |
| lines += dict_to_lines("Shop türleri:", shop_counts) | |
| lines += dict_to_lines("Highway türleri:", highway_counts) | |
| lines += dict_to_lines("Railway türleri:", railway_counts) | |
| lines += dict_to_lines("Public transport türleri:", pt_counts) | |
| if examples: | |
| lines.append("") | |
| lines.append(f"İlk {len(examples)} elementten bazı örnekler:") | |
| lines.extend(examples) | |
| return "\n".join(lines) | |
| # ========================================== | |
| # LLM SOHBET FONKSİYONU | |
| # ========================================== | |
| def respond( | |
| message, | |
| history, | |
| model_name, | |
| system_message, | |
| max_tokens, | |
| temperature, | |
| top_p, | |
| compare_context, # Mahalle karşılaştırma | |
| spatial_context, # Overpass sonuçlar | |
| ): | |
| # --------------- OSM SPATIAL QUERY MODU --------------- | |
| # --- /osm ile başlayan mesajlar: sadece Overpass sorgusu üret --- | |
| if is_osm_query(message): | |
| user_text = message.lstrip()[4:].strip() | |
| if not user_text: | |
| user_text = "Generate an Overpass QL query for my request." | |
| client = InferenceClient(model=model_name, token=HF_TOKEN if HF_TOKEN else None) | |
| messages = [ | |
| {"role": "system", "content": OSM_QUERY_SYSTEM}, | |
| {"role": "user", "content": user_text}, | |
| ] | |
| query_text = "" | |
| for chunk in client.chat_completion( | |
| messages=messages, | |
| max_tokens=400, | |
| stream=True, | |
| temperature=0.2, | |
| top_p=0.9, | |
| ): | |
| choices = chunk.choices | |
| token_text = "" | |
| if len(choices) and choices[0].delta.content: | |
| token_text = choices[0].delta.content | |
| query_text += token_text | |
| # Kullanıcıya sadece sorguyu göster | |
| yield f"Üretilen Overpass Sorgusu:\n```ql\n{query_text}\n```" | |
| return | |
| # --------------- NORMAL CHAT AKIŞI --------------- | |
| """ | |
| Streaming chat using Hugging Face Inference API. | |
| history: list of {"role": "...", "content": "..."} | |
| """ | |
| # Temperature güvenli aralık (model max 2) | |
| temperature = max(0.0, min(2.0, float(temperature))) | |
| top_p = max(0.0, min(1.0, float(top_p))) | |
| client = InferenceClient(model=model_name, token=HF_TOKEN if HF_TOKEN else None) | |
| # System mesajına mahalle karşılaştırma bağlamını ekle | |
| full_system = system_message | |
| if compare_context: | |
| full_system += ( | |
| "\n\nAşağıda aynı şehirdeki iki mahalleye ait sayısal özetler var.\n" | |
| "Kullanıcı bu mahalleler hakkında soru sorarsa bu bağlama göre cevap ver:\n" | |
| f"{compare_context}" | |
| ) | |
| if spatial_context: | |
| full_system += ( | |
| "\n\nAyrıca kullanıcı tarafından en son çalıştırılan bir Overpass (spatial) sorgusunun" | |
| " özet sonuçları var. Kullanıcı bu sorgudan gelen veriler hakkında soru sorarsa," | |
| " bu özet bağlamına dayanarak cevap ver:\n" | |
| f"{spatial_context}" | |
| ) | |
| messages = [{"role": "system", "content": full_system}] | |
| messages.extend(history) | |
| messages.append({"role": "user", "content": message}) | |
| response = "" | |
| for chunk in client.chat_completion( | |
| messages=messages, | |
| max_tokens=max_tokens, | |
| stream=True, | |
| temperature=temperature, | |
| top_p=top_p, | |
| ): | |
| choices = chunk.choices | |
| token_text = "" | |
| if len(choices) and choices[0].delta.content: | |
| token_text = choices[0].delta.content | |
| response += token_text | |
| yield response + f"\n\n---\n**Model:** {model_name}" | |
| # ========================================== | |
| # GRADIO ARAYÜZÜ (SOL CHAT, SAĞ KARŞILAŞTIRMA PANELİ) | |
| # ========================================== | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## Mahalle Karşılaştırmalı Chat Botu") | |
| compare_state = gr.State("") | |
| spatial_state = gr.State("") # son Overpass özetleri | |
| overpass_elements_state = gr.State([]) # 👈 tüm Overpass sonuçlarını biriktireceğimiz liste | |
| gdf1_state = gr.State(None) # 1. mahalle geometri | |
| gdf2_state = gr.State(None) # 2. mahalle geometri | |
| with gr.Row(): | |
| # SOL SÜTUN: CHAT | |
| with gr.Column(scale=2): | |
| chatbox = gr.Chatbot(height=800, scale=1) | |
| model_dropdown = gr.Dropdown( | |
| choices=[ | |
| # Küçük modeller | |
| "google/gemma-2-2b-it", # 2B | |
| "meta-llama/Meta-Llama-3.1-8B-Instruct", # 8B | |
| # Büyük modeller | |
| "abacusai/Dracarys-72B-Instruct", # 72B | |
| "Qwen/Qwen2.5-72B-Instruct", # 72B | |
| # Çok büyük model | |
| "openai/gpt-oss-120b", # 120Bdı | |
| ], | |
| label="Model Seç (Bu listedeki modeller Hugging Face Inference API chat_completion ile uyumludur)", | |
| value="google/gemma-2-2b-it" | |
| ) | |
| system_box = gr.Textbox( | |
| value="Sen şehir planlama ve mahalleler hakkında bilgi veren yardımsever bir asistansın.", | |
| label="System message", | |
| ) | |
| max_tokens_slider = gr.Slider( | |
| minimum=1, | |
| maximum=2048, | |
| value=512, | |
| step=1, | |
| label="Max new tokens", | |
| ) | |
| temperature_slider = gr.Slider( | |
| minimum=0.0, | |
| maximum=2.0, # model max 2 | |
| value=0.5, | |
| step=0.1, | |
| label="Temperature", | |
| ) | |
| top_p_slider = gr.Slider( | |
| minimum=0.1, | |
| maximum=1.0, | |
| value=0.5, | |
| step=0.05, | |
| label="Top-p (nucleus sampling)", | |
| ) | |
| chatbot = gr.ChatInterface( | |
| respond, | |
| chatbot=chatbox, | |
| type="messages", | |
| title="Basit Chat Botu", | |
| description="Küçük bir sohbet botu, HF Inference API ve OSM verisi ile çalışıyor.", | |
| additional_inputs=[ | |
| model_dropdown, | |
| system_box, | |
| max_tokens_slider, | |
| temperature_slider, | |
| top_p_slider, | |
| compare_state, # >>> mahalle karşılaştırma bağlamı | |
| spatial_state, # >>> son Overpass sonucu özeti | |
| ], | |
| ) | |
| # SAĞ SÜTUN: MAHALLE KARŞILAŞTIRMA PANELİ | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Mahalle Karşılaştırma") | |
| # 1. mahalle: ilçe + mahalle aynı satırda | |
| with gr.Row(): | |
| city_in1 = gr.Textbox( | |
| label="1. Şehir", | |
| value="Ankara", | |
| placeholder="Örn: Ankara", | |
| ) | |
| district1_in = gr.Textbox( | |
| label="1. İlçe", | |
| value="Gölbaşı", | |
| scale=1, | |
| placeholder="Örn: Gölbaşı", | |
| ) | |
| neigh1_in = gr.Textbox( | |
| label="1. Mahalle", | |
| value="İncek", | |
| scale=2, | |
| placeholder="Örn: İncek Mahallesi", | |
| ) | |
| # 2. mahalle: ilçe + mahalle aynı satırda | |
| with gr.Row(): | |
| city_in2 = gr.Textbox( | |
| label="2. Şehir", | |
| value="Ankara", | |
| placeholder="Örn: Ankara", | |
| ) | |
| district2_in = gr.Textbox( | |
| label="2. İlçe", | |
| value="Gölbaşı", | |
| scale=1, | |
| placeholder="Örn: Gölbaşı", | |
| ) | |
| neigh2_in = gr.Textbox( | |
| label="2. Mahalle", | |
| value="Kızılcaşar", | |
| scale=2, | |
| placeholder="Örn: Kızılcaşar Mahallesi", | |
| ) | |
| compare_btn = gr.Button("Karşılaştırmayı Hazırla") | |
| with gr.Row(): | |
| stats1_box = gr.Textbox( | |
| label="1. Mahalle Özeti", | |
| lines=4, | |
| ) | |
| stats2_box = gr.Textbox( | |
| label="2. Mahalle Özeti", | |
| lines=4, | |
| ) | |
| map_html = gr.HTML(label="Mahalle Haritası") | |
| gr.Markdown("### Spatial Query (Overpass)") | |
| # 1) LLM'e doğal dil prompt'u | |
| osm_nl_prompt = gr.Textbox( | |
| label="LLM ile Overpass Sorgusu (Doğal Dil)", | |
| lines=3, | |
| placeholder="Örn: İncek ve Kızılcaşar çevresindeki tüm park ve okulları getir" | |
| ) | |
| gen_and_run_btn = gr.Button("LLM ile Sorguyu Üret ve Çalıştır") | |
| # 2) Üretilen veya manuel Overpass sorgusu | |
| overpass_box = gr.Textbox( | |
| label="Overpass Sorgusu", | |
| lines=6, | |
| placeholder="Buraya LLM'in ürettiği Overpass QL sorgusunu yapıştırın..." | |
| ) | |
| layer_color_dd = gr.Dropdown( | |
| choices=["red", "blue", "green", "orange", "purple"], | |
| value="red", | |
| label="Spatial katman rengi" | |
| ) | |
| run_overpass_btn = gr.Button("Sorguyu Çalıştır ve Haritayı Güncelle") | |
| # En yakın POI (generic) | |
| poi_type_dropdown = gr.Dropdown( | |
| choices=["Üniversite", "Market", "Hastane", "Eczane", "Park"], | |
| value="Üniversite", | |
| label="En yakın neyi bulmak istiyorsun?" | |
| ) | |
| nearest_poi_btn = gr.Button("1. Mahalle için en yakın noktayı bul") | |
| nearest_poi_box = gr.Textbox( | |
| label="En Yakın POI", | |
| lines=2, | |
| ) | |
| nearest_poi_btn.click( | |
| fn=nearest_poi_wrapper, | |
| inputs=[city_in1, district1_in, neigh1_in, poi_type_dropdown], | |
| outputs=[nearest_poi_box], | |
| ) | |
| # LLM ile üret + çalıştır (birikimli) | |
| gen_and_run_btn.click( | |
| fn=llm_overpass_to_map, | |
| inputs=[osm_nl_prompt, model_dropdown, overpass_elements_state, | |
| spatial_state, layer_color_dd, gdf1_state, gdf2_state], | |
| outputs=[overpass_box, map_html, spatial_state, overpass_elements_state], | |
| ) | |
| # Manuel Overpass çalıştırma (birikimli) | |
| run_overpass_btn.click( | |
| fn=run_overpass_to_map, | |
| inputs=[overpass_box, overpass_elements_state, spatial_state, | |
| layer_color_dd, gdf1_state, gdf2_state], | |
| outputs=[map_html, spatial_state, overpass_elements_state], | |
| ) | |
| compare_btn.click( | |
| fn=prepare_comparison, | |
| inputs=[city_in1, district1_in, neigh1_in, city_in1, district2_in, neigh2_in], | |
| outputs=[stats1_box, stats2_box, compare_state, map_html, gdf1_state, gdf2_state], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |