Fix: Region-BBox aus VLM-Analyse + generische Tags filtern

Wenn keine Viewport-BBox gesetzt ist, nutzt der Query-Generator die
VLM-Regionsschaetzung (z.B. Europa, Naher Osten) als grobe BBox.
70+ Regionen gemappt (DE/EN). Generische OSM-Tags (building, highway,
landuse) werden gefiltert um Overpass-Timeouts zu vermeiden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
Claude Dev
2026-03-26 00:32:10 +01:00
Ursprung 03b9abc991
Commit 749c78b6a3
2 geänderte Dateien mit 113 neuen und 5 gelöschten Zeilen

Datei anzeigen

@@ -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