From 28a87f1bb5c1904b6491644d9c0208096c9ff561 Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Fri, 27 Mar 2026 23:15:54 +0100 Subject: [PATCH] fix: VLM-Bildanalyse Bugfixes (Prompt, UI, Logging) - Fix 1: Claude-Prompt weist generische Natur-Objekte ab (forest, mountain etc.) Objects-Array nur fuer OSINT-relevante Infrastruktur, Rest in terrain/landscape_clues - Fix 2: Freundliche UI-Meldungen bei leeren Objects, 0 Treffern, gefilterten Tags Button ausgegraut + Hinweis wenn keine Infrastruktur erkannt - Fix 3: generate_queries gibt JSON statt HTTP 400 bei leeren Fragments - Fix 4: Verbose Objekt-Match-Logging auf debug reduziert - Fix 5: Verwaiste static/js/ui/overpass.js geloescht Co-Authored-By: Claude Opus 4.6 (1M context) --- src/data_vlm.py | 14 +-- static/js/ui/overpass.js | 207 --------------------------------------- static/js/ui/vlm.js | 28 ++++++ 3 files changed, 35 insertions(+), 214 deletions(-) delete mode 100644 static/js/ui/overpass.js diff --git a/src/data_vlm.py b/src/data_vlm.py index cb8c82f..033a3fd 100644 --- a/src/data_vlm.py +++ b/src/data_vlm.py @@ -346,7 +346,7 @@ async def _run_claude(image_path: str, viewport_info: str = "", filename: str = "4) Bodenfarbe und Gestein. Heller Rheinkies vs. dunkler Donauschotter. " "5) Schatten: Richtung und Laenge. Vergleiche mit den Sonnenstand-Daten falls vorhanden. " "Fuelle identified_features (Gewaessername, Region, Landmarken) und landscape_clues komplett. " - "Antworte ausschliesslich im vorgegebenen JSON-Format." + "OBJEKT-REGELN: Das objects-Array darf NUR konkrete OSINT-relevante Infrastruktur enthalten: Flughaefen, Militaerbasen, Haefen, Kraftwerke, Bruecken, Radaranlagen, Raffinerien, Gefaengnisse, Botschaften, Bahnhoefe, Sendemasten, Staudaemme, Bunker, Hangars, Landebahnen etc. Generische Natur (forest, mountain, farmland, vegetation, residential_area, river, lake, field) gehoert in terrain/landscape_clues, NICHT in objects. Wenn das Bild keine OSINT-relevante Infrastruktur zeigt: objects als leeres Array [] zurueckgeben. Antworte ausschliesslich im vorgegebenen JSON-Format." ) cmd = [ @@ -603,7 +603,7 @@ async def generate_queries(req: QueryGenRequest): for obj in req.objects: obj_type = obj.get("type", "").lower().replace(" ", "_") osm_tags = obj.get("osm_tags", []) - logger.info(f"VLM Objekt: type={obj_type}, osm_tags={osm_tags}") + logger.debug(f"VLM Objekt: type={obj_type}, osm_tags={osm_tags}") # Direkte Zuordnung if obj_type in _OBJECT_TO_OVERPASS and obj_type not in used_types: @@ -611,7 +611,7 @@ async def generate_queries(req: QueryGenRequest): fragments.append(f' node{tag}{bbox_str};') fragments.append(f' way{tag}{bbox_str};') used_types.add(obj_type) - logger.info(f" -> Mapping-Match: {obj_type} -> {tag}") + logger.debug(f" -> Mapping-Match: {obj_type} -> {tag}") continue # Fallback: OSM-Tags aus VLM-Ergebnis verwenden @@ -620,20 +620,20 @@ async def generate_queries(req: QueryGenRequest): if "=" in tag_str and tag_str not in used_types: key, val = tag_str.split("=", 1) if key in _GENERIC_KEYS: - logger.info(f" -> Tag gefiltert (generisch): {tag_str}") + logger.debug(f" -> Tag gefiltert (generisch): {tag_str}") continue frag = f'["{key}"="{val}"]' fragments.append(f' node{frag}{bbox_str};') fragments.append(f' way{frag}{bbox_str};') used_types.add(tag_str) - logger.info(f" -> OSM-Tag verwendet: {tag_str}") + logger.debug(f" -> OSM-Tag verwendet: {tag_str}") matched = True break if not matched and obj_type not in _OBJECT_TO_OVERPASS: - logger.warning(f" -> KEIN MATCH fuer: {obj_type} (tags: {osm_tags})") + logger.debug(f" -> KEIN MATCH fuer: {obj_type} (tags: {osm_tags})") if not fragments: - raise HTTPException(400, "Keine passenden OSM-Tags fuer die erkannten Objekte gefunden") + return {"query": "", "tag_count": 0, "object_count": len(req.objects), "no_infrastructure": True} query = "[out:json][timeout:30];\n(\n" + "\n".join(fragments) + "\n);\nout center body;" diff --git a/static/js/ui/overpass.js b/static/js/ui/overpass.js deleted file mode 100644 index 83afc0b..0000000 --- a/static/js/ui/overpass.js +++ /dev/null @@ -1,207 +0,0 @@ -/** - * Overpass UI: Query-Editor-Panel mit Template-Browser und BBox-Integration. - */ -const OverpassUI = { - _panel: null, - _editor: null, - _templates: [], - _categories: [], - _activeCategory: null, - _isLoading: false, - _useBbox: true, - - init: function() { - this._createPanel(); - this._loadTemplates(); - }, - - show: function() { - if (!this._panel) this.init(); - this._panel.style.display = 'block'; - if (!OverpassLayer._points) OverpassLayer.start(Globe.viewer); - }, - - hide: function() { - if (this._panel) this._panel.style.display = 'none'; - }, - - _createPanel: function() { - var panel = document.getElementById('overpass-panel'); - if (!panel) return; - this._panel = panel; - - panel.innerHTML = - '
' + - '
OVERPASS QUERY
' + - '' + - '
' + - '
' + - '
' + - '
' + - '
OVERPASSQL EDITOR
' + - '' + - '
' + - '' + - '
' + - '' + - '' + - ''; - - this._editor = document.getElementById('overpass-editor'); - }, - - _loadTemplates: function() { - var self = this; - fetch('/api/overpass/templates') - .then(function(r) { return r.json(); }) - .then(function(data) { - self._categories = data.categories || []; - if (self._categories.length > 0) { - self._activeCategory = self._categories[0].id; - self._renderCategoryTabs(); - self._renderTemplateList(); - } - }) - .catch(function(e) { console.warn('Overpass Templates:', e); }); - }, - - _renderCategoryTabs: function() { - var el = document.getElementById('overpass-cat-tabs'); - if (!el) return; - var self = this; - var html = ''; - this._categories.forEach(function(cat) { - var active = cat.id === self._activeCategory ? ' active' : ''; - html += ''; - }); - el.innerHTML = html; - }, - - _selectCategory: function(catId) { - this._activeCategory = catId; - this._renderCategoryTabs(); - this._renderTemplateList(); - }, - - _renderTemplateList: function() { - var el = document.getElementById('overpass-template-list'); - if (!el) return; - var self = this; - var cat = this._categories.find(function(c) { return c.id === self._activeCategory; }); - if (!cat) { el.innerHTML = ''; return; } - - var html = ''; - cat.templates.forEach(function(t) { - html += ''; - }); - el.innerHTML = html; - }, - - _applyTemplate: function(templateId) { - var tpl = null; - for (var i = 0; i < this._categories.length; i++) { - var found = this._categories[i].templates.find(function(t) { return t.id === templateId; }); - if (found) { tpl = found; break; } - } - if (tpl && this._editor) { - this._editor.value = tpl.query; - OverpassLayer.setColor(tpl.color); - } - }, - - _getBboxFromViewport: function() { - if (!Globe.viewer) return null; - var rect = Globe.viewer.camera.computeViewRectangle(); - if (!rect) return null; - return [ - Cesium.Math.toDegrees(rect.south), - Cesium.Math.toDegrees(rect.west), - Cesium.Math.toDegrees(rect.north), - Cesium.Math.toDegrees(rect.east), - ]; - }, - - _executeQuery: function() { - if (this._isLoading || !this._editor) return; - var query = this._editor.value.trim(); - if (!query) return; - - var bbox = null; - if (this._useBbox) { - bbox = this._getBboxFromViewport(); - } - - this._isLoading = true; - var btn = document.getElementById('overpass-exec-btn'); - if (btn) { btn.disabled = true; btn.textContent = 'LADE...'; } - var resultEl = document.getElementById('overpass-result'); - if (resultEl) { resultEl.style.display = 'block'; resultEl.textContent = 'Anfrage wird gesendet...'; resultEl.style.color = 'var(--text-dim)'; } - var startTime = Date.now(); - - var self = this; - fetch('/api/overpass/query', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query: query, bbox: bbox }), - }) - .then(function(r) { - if (r.status === 429) { - return r.json().then(function(d) { throw new Error(d.detail || 'Rate-Limit erreicht'); }); - } - if (!r.ok) { - return r.json().then(function(d) { throw new Error(d.detail || 'Fehler ' + r.status); }); - } - return r.json(); - }) - .then(function(data) { - var elapsed = ((Date.now() - startTime) / 1000).toFixed(1); - OverpassLayer.render(data); - if (resultEl) { - var text = data.total + ' Objekte (' + elapsed + 's)'; - if (data.cached) text += ' [Cache]'; - if (data.truncated) text += ' [max. ' + data.total + ' angezeigt]'; - resultEl.textContent = text; - resultEl.style.color = 'var(--accent)'; - } - var clearBtn = document.getElementById('overpass-clear-btn'); - if (clearBtn) clearBtn.style.display = 'block'; - }) - .catch(function(e) { - if (resultEl) { - resultEl.textContent = 'Fehler: ' + e.message; - resultEl.style.color = '#ff5252'; - } - }) - .finally(function() { - self._isLoading = false; - if (btn) { btn.disabled = false; btn.textContent = 'AUSFUEHREN'; } - }); - }, - - _clearResults: function() { - OverpassLayer.clear(); - var resultEl = document.getElementById('overpass-result'); - if (resultEl) { resultEl.style.display = 'none'; resultEl.style.color = 'var(--text-dim)'; } - var clearBtn = document.getElementById('overpass-clear-btn'); - if (clearBtn) clearBtn.style.display = 'none'; - }, - - executeQueryDirect: function(query, bbox, color) { - if (this._editor) this._editor.value = query; - if (color) OverpassLayer.setColor(color); - this._useBbox = !!bbox; - var bboxCb = document.getElementById('overpass-bbox'); - if (bboxCb) bboxCb.checked = this._useBbox; - this.show(); - this._executeQuery(); - }, -}; diff --git a/static/js/ui/vlm.js b/static/js/ui/vlm.js index 005a26b..d348be5 100644 --- a/static/js/ui/vlm.js +++ b/static/js/ui/vlm.js @@ -62,6 +62,7 @@ const VlmUI = { '' + '' + '' + + '' + '' + // Reset '