diff --git a/src/data_vlm.py b/src/data_vlm.py index d5fa8f4..b9144ec 100644 --- a/src/data_vlm.py +++ b/src/data_vlm.py @@ -110,6 +110,87 @@ _OBJECT_TO_OVERPASS = { "bunker": '["military"="bunker"]', } +# --- Regionen -> BBox Mapping (grob) --- +_REGION_BBOX = { + # Europa + "europa": (-10, 34, 40, 72), + "europe": (-10, 34, 40, 72), + "westeuropa": (-10, 36, 15, 60), + "western europe": (-10, 36, 15, 60), + "osteuropa": (15, 42, 45, 62), + "eastern europe": (15, 42, 45, 62), + "nordeuropa": (5, 54, 30, 72), + "northern europe": (5, 54, 30, 72), + "suedeuropa": (-10, 34, 30, 48), + "southern europe": (-10, 34, 30, 48), + "mitteleuropa": (5, 45, 20, 56), + "central europe": (5, 45, 20, 56), + "deutschland": (5.8, 47.2, 15.1, 55.1), + "germany": (5.8, 47.2, 15.1, 55.1), + # Naher Osten + "naher osten": (25, 12, 65, 42), + "middle east": (25, 12, 65, 42), + "near east": (25, 12, 65, 42), + "persischer golf": (45, 22, 60, 32), + "persian gulf": (45, 22, 60, 32), + "levante": (34, 29, 42, 37), + "levant": (34, 29, 42, 37), + # Asien + "ostasien": (100, 20, 150, 55), + "east asia": (100, 20, 150, 55), + "suedasien": (60, 5, 100, 38), + "south asia": (60, 5, 100, 38), + "suedostasien": (95, -10, 140, 25), + "southeast asia": (95, -10, 140, 25), + "zentralasien": (50, 35, 80, 55), + "central asia": (50, 35, 80, 55), + "asien": (60, -10, 150, 55), + "asia": (60, -10, 150, 55), + # Afrika + "nordafrika": (-15, 15, 40, 38), + "north africa": (-15, 15, 40, 38), + "westafrika": (-20, 0, 15, 25), + "west africa": (-20, 0, 15, 25), + "ostafrika": (25, -12, 52, 18), + "east africa": (25, -12, 52, 18), + "suedliches afrika": (10, -35, 42, -5), + "southern africa": (10, -35, 42, -5), + "afrika": (-20, -35, 52, 38), + "africa": (-20, -35, 52, 38), + # Amerika + "nordamerika": (-170, 15, -50, 72), + "north america": (-170, 15, -50, 72), + "usa": (-125, 24, -66, 50), + "united states": (-125, 24, -66, 50), + "mittelamerika": (-120, 7, -60, 25), + "central america": (-120, 7, -60, 25), + "suedamerika": (-82, -56, -34, 13), + "south america": (-82, -56, -34, 13), + # Ozeanien + "ozeanien": (110, -50, 180, 0), + "oceania": (110, -50, 180, 0), + "australien": (112, -44, 154, -10), + "australia": (112, -44, 154, -10), + # Arktis/Antarktis + "arktis": (-180, 65, 180, 90), + "arctic": (-180, 65, 180, 90), +} + + +def _resolve_region_bbox(region_text): + """Versucht aus dem VLM-Regionstext eine BBox abzuleiten.""" + if not region_text: + return None + text = region_text.lower().strip() + # Exakter Match + if text in _REGION_BBOX: + return _REGION_BBOX[text] + # Teilstring-Match (z.B. "vermutlich Europa" -> "europa") + for key, bbox in _REGION_BBOX.items(): + if key in text: + return bbox + return None + def _resize_image(input_path: str, output_path: str): """Skaliert Bild auf max _MAX_IMAGE_DIMENSION px.""" @@ -251,6 +332,7 @@ def _ext(content_type: str) -> str: class QueryGenRequest(BaseModel): objects: list[dict] bbox: list[float] | None = None + estimated_location_type: str | None = None @router.post("/vlm/generate-queries") @@ -259,9 +341,21 @@ async def generate_queries(req: QueryGenRequest): if not req.objects: raise HTTPException(400, "Keine Objekte angegeben") + # BBox bestimmen: explizit > Region > weltweit + bbox = req.bbox + region_used = None + if not bbox and req.estimated_location_type: + region_bbox = _resolve_region_bbox(req.estimated_location_type) + if region_bbox: + bbox = [region_bbox[1], region_bbox[0], region_bbox[3], region_bbox[2]] # S,W,N,E + region_used = req.estimated_location_type + bbox_str = "" - if req.bbox and len(req.bbox) == 4: - bbox_str = f"({req.bbox[0]},{req.bbox[1]},{req.bbox[2]},{req.bbox[3]})" + if bbox and len(bbox) == 4: + bbox_str = f"({bbox[0]},{bbox[1]},{bbox[2]},{bbox[3]})" + + if not bbox_str: + logger.warning("Overpass-Query ohne BBox - kann bei generischen Tags Timeout verursachen") fragments = [] used_types = set() @@ -278,10 +372,19 @@ async def generate_queries(req: QueryGenRequest): continue # Fallback: OSM-Tags aus VLM-Ergebnis verwenden + # Generische Tags ignorieren (zu viele Treffer, Overpass Timeout) + _GENERIC_KEYS = { + "building", "highway", "landuse", "natural", "waterway", + "surface", "barrier", "wall", "fence", "roof", + "addr:street", "addr:city", "addr:housenumber", + "name", "source", "type", + } osm_tags = obj.get("osm_tags", []) for tag_str in osm_tags: if "=" in tag_str and tag_str not in used_types: key, val = tag_str.split("=", 1) + if key in _GENERIC_KEYS: + continue # Zu generisch, ueberspringen frag = f'["{key}"="{val}"]' fragments.append(f' node{frag}{bbox_str};') fragments.append(f' way{frag}{bbox_str};') @@ -293,8 +396,12 @@ async def generate_queries(req: QueryGenRequest): query = "[out:json][timeout:30];\n(\n" + "\n".join(fragments) + "\n);\nout center body;" - return { + result = { "query": query, "object_count": len(req.objects), "tag_count": len(used_types), } + if region_used: + result["region"] = region_used + logger.info(f"VLM Query: Region '{region_used}' als BBox verwendet") + return result diff --git a/static/js/ui/vlm.js b/static/js/ui/vlm.js index 62a0471..dbf646c 100644 --- a/static/js/ui/vlm.js +++ b/static/js/ui/vlm.js @@ -272,14 +272,15 @@ const VlmUI = { fetch('/api/vlm/generate-queries', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ objects: selected, bbox: bbox }), + body: JSON.stringify({ objects: selected, bbox: bbox, estimated_location_type: self._currentAnalysis.estimated_location_type || null }), }) .then(function(r) { if (!r.ok) return r.json().then(function(d) { throw new Error(d.detail || 'Fehler'); }); return r.json(); }) .then(function(data) { - if (searchResult) searchResult.textContent = 'Query generiert (' + data.tag_count + ' Tags). Suche OSM-Daten...'; + var regionInfo = data.region ? ' (Region: ' + data.region + ')' : ' (weltweit)'; + if (searchResult) searchResult.textContent = 'Query generiert (' + data.tag_count + ' Tags)' + regionInfo + '. Suche OSM-Daten...'; // 2. Overpass-Query direkt ausfuehren OverpassLayer.setColor('#e040fb');