feat: Globe-Suche nutzt jetzt EXIF + Region + Overpass kombiniert
- EXIF-GPS: Marker direkt auf Globus + Kamera fliegt hin - EXIF-GPS als BBox (0.5 Grad Radius) fuer praezisere Overpass-Suche - Region aus VLM/Landscape Clues als Fallback-BBox - Statusanzeige zeigt alle genutzten Datenquellen - Overpass-Ergebnisse werden mit EXIF-Marker kombiniert
Dieser Commit ist enthalten in:
@@ -279,9 +279,10 @@ const VlmUI = {
|
|||||||
|
|
||||||
_confirmAndSearch: function() {
|
_confirmAndSearch: function() {
|
||||||
if (!this._currentAnalysis) return;
|
if (!this._currentAnalysis) return;
|
||||||
|
var analysis = this._currentAnalysis;
|
||||||
|
|
||||||
// Ausgewaehlte Objekte sammeln
|
// Ausgewaehlte Objekte sammeln
|
||||||
var objects = this._currentAnalysis.objects || [];
|
var objects = analysis.objects || [];
|
||||||
var selected = [];
|
var selected = [];
|
||||||
objects.forEach(function(obj, idx) {
|
objects.forEach(function(obj, idx) {
|
||||||
var cb = document.getElementById('vlm-obj-' + idx);
|
var cb = document.getElementById('vlm-obj-' + idx);
|
||||||
@@ -295,42 +296,90 @@ const VlmUI = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// BBox: Standard weltweit (null), optional auf Viewport einschraenken
|
// === EXIF-GPS: Marker setzen + dorthin fliegen ===
|
||||||
|
var exif = analysis.exif;
|
||||||
|
var hasExifGps = exif && exif.has_gps && exif.latitude && exif.longitude;
|
||||||
|
if (hasExifGps) {
|
||||||
|
// EXIF-Position als Marker auf dem Globus
|
||||||
|
OverpassLayer.start(Globe.viewer);
|
||||||
|
OverpassLayer.setColor('#e040fb');
|
||||||
|
var exifNode = {
|
||||||
|
nodes: [{
|
||||||
|
id: 'exif-gps',
|
||||||
|
lat: exif.latitude,
|
||||||
|
lon: exif.longitude,
|
||||||
|
tags: {
|
||||||
|
'source': 'EXIF GPS',
|
||||||
|
'camera': (exif.camera_make || '') + ' ' + (exif.camera_model || ''),
|
||||||
|
'timestamp': exif.timestamp || '',
|
||||||
|
'altitude': exif.altitude ? exif.altitude + 'm' : '',
|
||||||
|
},
|
||||||
|
name: 'EXIF GPS-Position',
|
||||||
|
}],
|
||||||
|
ways: [],
|
||||||
|
relations: [],
|
||||||
|
total: 1,
|
||||||
|
};
|
||||||
|
OverpassLayer.render(exifNode);
|
||||||
|
// Zur EXIF-Position fliegen
|
||||||
|
Globe.viewer.camera.flyTo({
|
||||||
|
destination: Cesium.Cartesian3.fromDegrees(exif.longitude, exif.latitude, 50000),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// === BBox bestimmen: EXIF-GPS > Viewport-Checkbox > Region ===
|
||||||
var bbox = null;
|
var bbox = null;
|
||||||
var bboxCb = document.getElementById('vlm-use-bbox');
|
if (hasExifGps) {
|
||||||
if (bboxCb && bboxCb.checked && Globe.viewer) {
|
// 0.5 Grad um EXIF-Position
|
||||||
var rect = Globe.viewer.camera.computeViewRectangle();
|
bbox = [
|
||||||
if (rect) {
|
exif.latitude - 0.5,
|
||||||
bbox = [
|
exif.longitude - 0.5,
|
||||||
Cesium.Math.toDegrees(rect.south),
|
exif.latitude + 0.5,
|
||||||
Cesium.Math.toDegrees(rect.west),
|
exif.longitude + 0.5,
|
||||||
Cesium.Math.toDegrees(rect.north),
|
];
|
||||||
Cesium.Math.toDegrees(rect.east),
|
} else {
|
||||||
];
|
var bboxCb = document.getElementById('vlm-use-bbox');
|
||||||
|
if (bboxCb && bboxCb.checked && Globe.viewer) {
|
||||||
|
var rect = Globe.viewer.camera.computeViewRectangle();
|
||||||
|
if (rect) {
|
||||||
|
bbox = [
|
||||||
|
Cesium.Math.toDegrees(rect.south),
|
||||||
|
Cesium.Math.toDegrees(rect.west),
|
||||||
|
Cesium.Math.toDegrees(rect.north),
|
||||||
|
Cesium.Math.toDegrees(rect.east),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Kein explizites BBox? Region wird vom Backend aus estimated_location_type abgeleitet
|
||||||
}
|
}
|
||||||
|
|
||||||
var btn = document.getElementById('vlm-search-btn');
|
var btn = document.getElementById('vlm-search-btn');
|
||||||
if (btn) { btn.disabled = true; btn.textContent = 'SUCHE LAEUFT...'; }
|
if (btn) { btn.disabled = true; btn.textContent = 'SUCHE LAEUFT...'; }
|
||||||
var searchResult = document.getElementById('vlm-search-result');
|
var searchResult = document.getElementById('vlm-search-result');
|
||||||
if (searchResult) { searchResult.style.display = 'block'; searchResult.textContent = 'Generiere Overpass-Queries...'; searchResult.style.color = 'var(--text-dim)'; }
|
var statusParts = [];
|
||||||
|
if (hasExifGps) statusParts.push('EXIF GPS-Marker gesetzt');
|
||||||
|
if (searchResult) { searchResult.style.display = 'block'; searchResult.textContent = statusParts.join(' | ') + (statusParts.length ? ' | ' : '') + 'Generiere Overpass-Queries...'; searchResult.style.color = 'var(--text-dim)'; }
|
||||||
|
|
||||||
// 1. Queries generieren
|
// === Overpass-Suche mit allen verfuegbaren Daten ===
|
||||||
var startTime = Date.now();
|
var startTime = Date.now();
|
||||||
fetch('/api/vlm/generate-queries', {
|
fetch('/api/vlm/generate-queries', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ objects: selected, bbox: bbox, estimated_location_type: VlmUI._currentAnalysis.estimated_location_type || null }),
|
body: JSON.stringify({
|
||||||
|
objects: selected,
|
||||||
|
bbox: bbox,
|
||||||
|
estimated_location_type: analysis.estimated_location_type || null,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
.then(function(r) {
|
.then(function(r) {
|
||||||
if (!r.ok) return r.json().then(function(d) { throw new Error(d.detail || 'Fehler'); });
|
if (!r.ok) return r.json().then(function(d) { throw new Error(d.detail || 'Fehler'); });
|
||||||
return r.json();
|
return r.json();
|
||||||
})
|
})
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
var regionInfo = data.region ? ' (Region: ' + data.region + ')' : ' (weltweit)';
|
var src = hasExifGps ? 'EXIF BBox' : (data.region ? 'Region: ' + data.region : 'weltweit');
|
||||||
if (searchResult) searchResult.textContent = 'Query generiert (' + data.tag_count + ' Tags)' + regionInfo + '. Suche OSM-Daten...';
|
if (searchResult) searchResult.textContent = statusParts.join(' | ') + (statusParts.length ? ' | ' : '') + 'Query generiert (' + data.tag_count + ' Tags, ' + src + '). Suche OSM-Daten...';
|
||||||
|
|
||||||
// 2. Overpass-Query direkt ausfuehren
|
|
||||||
OverpassLayer.setColor('#e040fb');
|
OverpassLayer.setColor('#e040fb');
|
||||||
return fetch('/api/overpass/query', {
|
return fetch('/api/overpass/query', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -343,27 +392,47 @@ const VlmUI = {
|
|||||||
return r.json().then(function(d) { throw new Error(d.detail || 'Rate-Limit'); });
|
return r.json().then(function(d) { throw new Error(d.detail || 'Rate-Limit'); });
|
||||||
}
|
}
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
return r.json().then(function(d) { throw new Error(d.detail || 'Overpass-Fehler'); });
|
return r.text().then(function(txt) {
|
||||||
|
try { var d = JSON.parse(txt); throw new Error(d.detail || 'Overpass-Fehler'); }
|
||||||
|
catch(e) { if (e.message && !e.message.startsWith('Unexpected')) throw e; throw new Error('Overpass-Fehler ' + r.status); }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return r.json();
|
return r.json();
|
||||||
})
|
})
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
var elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
var elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||||
OverpassLayer.render(data);
|
// Bei EXIF-GPS: Overpass-Ergebnisse zu bestehendem Marker hinzufuegen
|
||||||
|
if (hasExifGps && data.total > 0) {
|
||||||
|
// Bestehende EXIF-Daten beibehalten, Overpass hinzufuegen
|
||||||
|
var combined = {
|
||||||
|
nodes: [{ id: 'exif-gps', lat: exif.latitude, lon: exif.longitude, tags: { 'source': 'EXIF GPS' }, name: 'EXIF GPS-Position' }].concat(data.nodes || []),
|
||||||
|
ways: data.ways || [],
|
||||||
|
relations: data.relations || [],
|
||||||
|
total: 1 + data.total,
|
||||||
|
};
|
||||||
|
OverpassLayer.render(combined);
|
||||||
|
} else if (!hasExifGps) {
|
||||||
|
OverpassLayer.render(data);
|
||||||
|
}
|
||||||
|
|
||||||
if (searchResult) {
|
if (searchResult) {
|
||||||
var text = data.total + ' Orte gefunden (' + elapsed + 's)';
|
var parts = [];
|
||||||
|
if (hasExifGps) parts.push('EXIF GPS-Marker');
|
||||||
|
parts.push(data.total + ' OSM-Orte');
|
||||||
|
var text = parts.join(' + ') + ' (' + elapsed + 's)';
|
||||||
if (data.cached) text += ' [Cache]';
|
if (data.cached) text += ' [Cache]';
|
||||||
if (data.truncated) text += ' [Limit erreicht]';
|
if (data.truncated) text += ' [Limit]';
|
||||||
searchResult.textContent = text;
|
searchResult.textContent = text;
|
||||||
searchResult.style.color = 'var(--accent)';
|
searchResult.style.color = 'var(--accent)';
|
||||||
}
|
}
|
||||||
var countEl = document.getElementById('count-vlm');
|
var countEl = document.getElementById('count-vlm');
|
||||||
if (countEl) countEl.textContent = data.total;
|
if (countEl) countEl.textContent = (hasExifGps ? 1 : 0) + data.total;
|
||||||
})
|
})
|
||||||
.catch(function(e) {
|
.catch(function(e) {
|
||||||
if (searchResult) {
|
if (searchResult) {
|
||||||
searchResult.textContent = 'Fehler: ' + e.message;
|
var prefix = hasExifGps ? 'EXIF GPS-Marker gesetzt | ' : '';
|
||||||
searchResult.style.color = '#ff5252';
|
searchResult.textContent = prefix + 'Overpass-Fehler: ' + e.message;
|
||||||
|
searchResult.style.color = hasExifGps ? '#ff9800' : '#ff5252';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(function() {
|
.finally(function() {
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren