/** * Funkmasten-Layer: Mobilfunkmasten aus OpenStreetMap via Overpass. */ const CelltowersLayer = { _viewer: null, _points: null, _labels: null, _count: 0, _interval: null, _lastBbox: null, start: function(viewer) { if (this._points) return; this._viewer = viewer; this._points = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection()); this._labels = viewer.scene.primitives.add(new Cesium.LabelCollection()); this._fetch(); var self = this; this._interval = setInterval(function() { self._fetchIfMoved(); }, 8000); }, stop: function() { if (this._interval) { clearInterval(this._interval); this._interval = null; } if (this._points && this._viewer) { this._viewer.scene.primitives.remove(this._points); this._points = null; } if (this._labels && this._viewer) { this._viewer.scene.primitives.remove(this._labels); this._labels = null; } this._count = 0; this._lastBbox = null; var c = document.getElementById('count-celltowers'); if (c) c.textContent = '-'; }, _getBbox: function() { var rect = this._viewer.camera.computeViewRectangle(); if (!rect) return null; return { south: Cesium.Math.toDegrees(rect.south), west: Cesium.Math.toDegrees(rect.west), north: Cesium.Math.toDegrees(rect.north), east: Cesium.Math.toDegrees(rect.east), }; }, _fetchIfMoved: function() { var bbox = this._getBbox(); if (!bbox) return; // Nur bei deutlicher Bewegung neu laden if (this._lastBbox) { var dLat = Math.abs(bbox.south - this._lastBbox.south) + Math.abs(bbox.north - this._lastBbox.north); var dLon = Math.abs(bbox.west - this._lastBbox.west) + Math.abs(bbox.east - this._lastBbox.east); if (dLat + dLon < 0.5) return; } // Nur bei Zoom nah genug (< ~500 km Sichtfeld) var cam = this._viewer.camera.positionCartographic; if (cam && cam.height > 600000) return; this._fetch(); }, _fetch: function() { var bbox = this._getBbox(); if (!bbox) return; // Viewport zu gross -> nicht laden if (Math.abs(bbox.north - bbox.south) > 5 || Math.abs(bbox.east - bbox.west) > 5) { var s = document.getElementById('status-celltowers'); if (s) { s.textContent = 'Naeher heranzoomen'; s.classList.add('active'); } return; } this._lastBbox = bbox; var self = this; fetch('/api/geoint/celltowers', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(bbox), }) .then(function(r) { return r.json(); }) .then(function(data) { self._render(data.towers || []); }) .catch(function() {}); }, _render: function(towers) { if (!this._points) return; this._points.removeAll(); this._labels.removeAll(); var color = Cesium.Color.fromCssColorString('#e040fb'); var colorDim = color.withAlpha(0.6); for (var i = 0; i < towers.length; i++) { var t = towers[i]; this._points.add({ position: Cesium.Cartesian3.fromDegrees(t.lon, t.lat, 0), pixelSize: 6, color: color, outlineColor: Cesium.Color.BLACK, outlineWidth: 1, }); var label = t.operator || t.name || ''; if (label && towers.length < 200) { this._labels.add({ position: Cesium.Cartesian3.fromDegrees(t.lon, t.lat, 0), text: label, font: '9px monospace', fillColor: colorDim, outlineColor: Cesium.Color.BLACK, outlineWidth: 2, style: Cesium.LabelStyle.FILL_AND_OUTLINE, pixelOffset: new Cesium.Cartesian2(6, -4), scale: 0.6, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 100000), }); } } this._count = towers.length; var c = document.getElementById('count-celltowers'); if (c) c.textContent = this._count.toLocaleString('de-DE'); var s = document.getElementById('status-celltowers'); if (s) { s.textContent = ''; s.classList.remove('active'); } }, };