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:
113
src/data_vlm.py
113
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
|
||||
|
||||
@@ -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');
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren