diff --git a/src/data_vlm.py b/src/data_vlm.py
index 7411ed4..286aa02 100644
--- a/src/data_vlm.py
+++ b/src/data_vlm.py
@@ -439,7 +439,8 @@ async def generate_queries(req: QueryGenRequest):
}
if region_used:
result["region"] = region_used
- logger.info(f"VLM Query generiert: {query}")
- if region_used:
logger.info(f"VLM Query: Region '{region_used}' als BBox verwendet")
+ # Effektive BBox zurueckgeben fuer Visualisierung
+ if bbox and len(bbox) == 4:
+ result["effective_bbox"] = {"south": bbox[0], "west": bbox[1], "north": bbox[2], "east": bbox[3]}
return result
diff --git a/static/js/ui/vlm.js b/static/js/ui/vlm.js
index 6d0e610..d0e9d5e 100644
--- a/static/js/ui/vlm.js
+++ b/static/js/ui/vlm.js
@@ -6,6 +6,8 @@ const VlmUI = {
_dropZone: null,
_currentAnalysis: null,
_isAnalyzing: false,
+ _searchAreaEntity: null,
+ _exifMarkerEntity: null,
init: function() {
this._createPanel();
@@ -300,27 +302,47 @@ const VlmUI = {
var exif = analysis.exif;
var hasExifGps = exif && exif.has_gps && exif.latitude && exif.longitude;
if (hasExifGps) {
- // EXIF-Position als Marker auf dem Globus
+ // EXIF-Position als hervorgehobener 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);
+ this._exifMarkerEntity = Globe.viewer.entities.add({
+ name: 'EXIF GPS-Position',
+ position: Cesium.Cartesian3.fromDegrees(exif.longitude, exif.latitude, 0),
+ point: {
+ pixelSize: 12,
+ color: Cesium.Color.fromCssColorString('#e040fb'),
+ outlineColor: Cesium.Color.WHITE,
+ outlineWidth: 2,
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ },
+ ellipse: {
+ semiMinorAxis: 500,
+ semiMajorAxis: 500,
+ material: Cesium.Color.fromCssColorString('#e040fb').withAlpha(0.15),
+ outline: true,
+ outlineColor: Cesium.Color.fromCssColorString('#e040fb').withAlpha(0.5),
+ },
+ label: {
+ text: 'EXIF GPS',
+ font: '12px monospace',
+ fillColor: Cesium.Color.fromCssColorString('#e040fb'),
+ outlineColor: Cesium.Color.BLACK,
+ outlineWidth: 3,
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
+ pixelOffset: new Cesium.Cartesian2(0, -20),
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ },
+ description: '
' +
+ '
EXIF GPS-Position
' +
+ '
' +
+ '| Latitude | ' + exif.latitude + ' |
' +
+ '| Longitude | ' + exif.longitude + ' |
' +
+ (exif.altitude ? '| Hoehe | ' + exif.altitude + 'm |
' : '') +
+ (exif.camera_model ? '| Kamera | ' + (exif.camera_make || '') + ' ' + exif.camera_model + ' |
' : '') +
+ (exif.timestamp ? '| Zeitstempel | ' + exif.timestamp + ' |
' : '') +
+ (exif.compass_heading ? '| Heading | ' + exif.compass_heading + '\u00B0 |
' : '') +
+ '
',
+ });
// Zur EXIF-Position fliegen
Globe.viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(exif.longitude, exif.latitude, 50000),
@@ -363,6 +385,7 @@ const VlmUI = {
// === Overpass-Suche mit allen verfuegbaren Daten ===
var startTime = Date.now();
+ var self = this;
fetch('/api/vlm/generate-queries', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -380,6 +403,12 @@ const VlmUI = {
var src = hasExifGps ? 'EXIF BBox' : (data.region ? 'Region: ' + data.region : 'weltweit');
if (searchResult) searchResult.textContent = statusParts.join(' | ') + (statusParts.length ? ' | ' : '') + 'Query generiert (' + data.tag_count + ' Tags, ' + src + '). Suche OSM-Daten...';
+ // Suchbereich auf dem Globus zeichnen
+ var drawBbox = data.effective_bbox || (bbox ? {south: bbox[0], west: bbox[1], north: bbox[2], east: bbox[3]} : null);
+ if (drawBbox) {
+ self._drawSearchArea(drawBbox, hasExifGps ? 'EXIF' : (data.region || 'Analyse'));
+ }
+
OverpassLayer.setColor('#e040fb');
return fetch('/api/overpass/query', {
method: 'POST',
@@ -401,24 +430,14 @@ const VlmUI = {
})
.then(function(data) {
var elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
- // 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);
- }
+ // Overpass-Ergebnisse rendern (EXIF-Marker ist separate Entity)
+ OverpassLayer.render(data);
if (searchResult) {
var parts = [];
- if (hasExifGps) parts.push('EXIF GPS-Marker');
- parts.push(data.total + ' OSM-Orte');
+ if (hasExifGps) parts.push('EXIF-Marker');
+ if (self._searchAreaEntity) parts.push('Suchbereich');
+ parts.push(data.total + ' OSM-Treffer');
var text = parts.join(' + ') + ' (' + elapsed + 's)';
if (data.cached) text += ' [Cache]';
if (data.truncated) text += ' [Limit]';
@@ -426,7 +445,7 @@ const VlmUI = {
searchResult.style.color = 'var(--accent)';
}
var countEl = document.getElementById('count-vlm');
- if (countEl) countEl.textContent = (hasExifGps ? 1 : 0) + data.total;
+ if (countEl) countEl.textContent = data.total;
})
.catch(function(e) {
if (searchResult) {
@@ -440,9 +459,99 @@ const VlmUI = {
});
},
+ // === Suchbereich auf dem Globus visualisieren ===
+ _drawSearchArea: function(bbox, label) {
+ this._clearSearchArea();
+ if (!Globe.viewer || !bbox) return;
+
+ var isSmall = (Math.abs(bbox.north - bbox.south) < 2 && Math.abs(bbox.east - bbox.west) < 2);
+ var fillAlpha = isSmall ? 0.12 : 0.06;
+ var outlineAlpha = isSmall ? 0.7 : 0.4;
+
+ this._searchAreaEntity = Globe.viewer.entities.add({
+ name: 'Suchbereich: ' + label,
+ rectangle: {
+ coordinates: Cesium.Rectangle.fromDegrees(bbox.west, bbox.south, bbox.east, bbox.north),
+ material: Cesium.Color.fromCssColorString('#e040fb').withAlpha(fillAlpha),
+ outline: true,
+ outlineColor: Cesium.Color.fromCssColorString('#e040fb').withAlpha(outlineAlpha),
+ outlineWidth: 2,
+ height: 0,
+ },
+ });
+
+ // Label in der Mitte des Bereichs
+ var centerLat = (bbox.south + bbox.north) / 2;
+ var centerLon = (bbox.west + bbox.east) / 2;
+ this._searchAreaLabel = Globe.viewer.entities.add({
+ position: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 0),
+ label: {
+ text: 'SUCHBEREICH',
+ font: '11px monospace',
+ fillColor: Cesium.Color.fromCssColorString('#e040fb').withAlpha(0.7),
+ outlineColor: Cesium.Color.BLACK,
+ outlineWidth: 3,
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
+ verticalOrigin: Cesium.VerticalOrigin.CENTER,
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
+ distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, isSmall ? 500000 : 8000000),
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ },
+ });
+
+ // Eckpunkte-Markierungen
+ this._searchAreaCorners = [];
+ var corners = [
+ [bbox.west, bbox.south], [bbox.east, bbox.south],
+ [bbox.east, bbox.north], [bbox.west, bbox.north],
+ ];
+ for (var i = 0; i < corners.length; i++) {
+ this._searchAreaCorners.push(Globe.viewer.entities.add({
+ position: Cesium.Cartesian3.fromDegrees(corners[i][0], corners[i][1], 0),
+ point: {
+ pixelSize: 4,
+ color: Cesium.Color.fromCssColorString('#e040fb').withAlpha(0.6),
+ outlineColor: Cesium.Color.BLACK,
+ outlineWidth: 1,
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ },
+ }));
+ }
+
+ // Kamera auf den Bereich ausrichten wenn kein EXIF-GPS (EXIF fliegt bereits hin)
+ if (!isSmall) {
+ Globe.viewer.camera.flyTo({
+ destination: Cesium.Rectangle.fromDegrees(bbox.west - 2, bbox.south - 2, bbox.east + 2, bbox.north + 2),
+ duration: 2,
+ });
+ }
+ },
+
+ _clearSearchArea: function() {
+ if (this._searchAreaEntity && Globe.viewer) {
+ Globe.viewer.entities.remove(this._searchAreaEntity);
+ this._searchAreaEntity = null;
+ }
+ if (this._searchAreaLabel && Globe.viewer) {
+ Globe.viewer.entities.remove(this._searchAreaLabel);
+ this._searchAreaLabel = null;
+ }
+ if (this._searchAreaCorners) {
+ for (var i = 0; i < this._searchAreaCorners.length; i++) {
+ if (Globe.viewer) Globe.viewer.entities.remove(this._searchAreaCorners[i]);
+ }
+ this._searchAreaCorners = null;
+ }
+ if (this._exifMarkerEntity && Globe.viewer) {
+ Globe.viewer.entities.remove(this._exifMarkerEntity);
+ this._exifMarkerEntity = null;
+ }
+ },
+
_reset: function() {
this._currentAnalysis = null;
this._isAnalyzing = false;
+ this._clearSearchArea();
if (this._dropZone) this._dropZone.style.display = 'block';