/** * Overpass Layer: Rendert Overpass-API-Ergebnisse auf dem CesiumJS-Globus. * Nodes als Punkte, Ways als Linien, Relations als Punkte (Center). */ const OverpassLayer = { _viewer: null, _points: null, _labels: null, _polylines: null, _count: 0, _data: null, _handler: null, _nodeIndex: [], _currentColor: '#ff9800', start(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._polylines = viewer.scene.primitives.add(new Cesium.PolylineCollection()); this._setupClickHandler(); }, stop() { if (this._handler) { this._handler.destroy(); this._handler = 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; } if (this._polylines && this._viewer) { this._viewer.scene.primitives.remove(this._polylines); this._polylines = null; } this._count = 0; this._data = null; this._nodeIndex = []; var countEl = document.getElementById('count-overpass'); if (countEl) countEl.textContent = '-'; }, clear() { if (this._points) this._points.removeAll(); if (this._labels) this._labels.removeAll(); if (this._polylines) this._polylines.removeAll(); this._count = 0; this._data = null; this._nodeIndex = []; var countEl = document.getElementById('count-overpass'); if (countEl) countEl.textContent = '-'; }, setColor(hexColor) { this._currentColor = hexColor || '#ff9800'; }, render(data) { this.clear(); if (!this._points) return; this._data = data; var color = Cesium.Color.fromCssColorString(this._currentColor); var colorDim = color.withAlpha(0.6); // Nodes var nodes = data.nodes || []; for (var i = 0; i < nodes.length; i++) { var n = nodes[i]; if (!n.lat || !n.lon) continue; this._points.add({ position: Cesium.Cartesian3.fromDegrees(n.lon, n.lat, 0), pixelSize: 7, color: color, outlineColor: Cesium.Color.BLACK, outlineWidth: 1, }); this._nodeIndex.push({ lat: n.lat, lon: n.lon, tags: n.tags, name: n.name, type: 'node' }); if (n.name) { this._labels.add({ position: Cesium.Cartesian3.fromDegrees(n.lon, n.lat, 0), text: n.name, font: '10px monospace', fillColor: color.withAlpha(0.8), outlineColor: Cesium.Color.BLACK, outlineWidth: 2, style: Cesium.LabelStyle.FILL_AND_OUTLINE, pixelOffset: new Cesium.Cartesian2(6, -4), scale: 0.7, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 500000), }); } } // Ways var ways = data.ways || []; for (var j = 0; j < ways.length; j++) { var w = ways[j]; if (w.geometry && w.geometry.length > 1) { var positions = []; for (var k = 0; k < w.geometry.length; k++) { var g = w.geometry[k]; if (g.lat && g.lon) { positions.push(Cesium.Cartesian3.fromDegrees(g.lon, g.lat, 0)); } } if (positions.length > 1) { this._polylines.add({ positions: positions, width: 2.5, material: Cesium.Material.fromType('Color', { color: colorDim }), }); } } if (w.lat && w.lon) { this._points.add({ position: Cesium.Cartesian3.fromDegrees(w.lon, w.lat, 0), pixelSize: 5, color: colorDim, }); this._nodeIndex.push({ lat: w.lat, lon: w.lon, tags: w.tags, name: w.name, type: 'way' }); if (w.name) { this._labels.add({ position: Cesium.Cartesian3.fromDegrees(w.lon, w.lat, 0), text: w.name, font: '10px monospace', fillColor: color.withAlpha(0.7), outlineColor: Cesium.Color.BLACK, outlineWidth: 2, style: Cesium.LabelStyle.FILL_AND_OUTLINE, pixelOffset: new Cesium.Cartesian2(6, -4), scale: 0.7, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 500000), }); } } } // Relations (Center-Punkt) var rels = data.relations || []; for (var m = 0; m < rels.length; m++) { var r = rels[m]; if (!r.lat || !r.lon) continue; this._points.add({ position: Cesium.Cartesian3.fromDegrees(r.lon, r.lat, 0), pixelSize: 9, color: color, outlineColor: color.withAlpha(0.3), outlineWidth: 3, }); this._nodeIndex.push({ lat: r.lat, lon: r.lon, tags: r.tags, name: r.name, type: 'relation' }); if (r.name) { this._labels.add({ position: Cesium.Cartesian3.fromDegrees(r.lon, r.lat, 0), text: r.name, font: '11px monospace', fillColor: color, outlineColor: Cesium.Color.BLACK, outlineWidth: 2, style: Cesium.LabelStyle.FILL_AND_OUTLINE, pixelOffset: new Cesium.Cartesian2(8, -6), scale: 0.8, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 800000), }); } } this._count = nodes.length + ways.length + rels.length; var countEl = document.getElementById('count-overpass'); if (countEl) countEl.textContent = this._count.toLocaleString('de-DE'); }, _setupClickHandler() { var self = this; this._handler = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas); this._handler.setInputAction(function(click) { if (!self._nodeIndex.length) return; var cart = self._viewer.scene.pickPosition(click.position); if (!cart) { var ray = self._viewer.scene.camera.getPickRay(click.position); cart = self._viewer.scene.globe.pick(ray, self._viewer.scene); } if (!cart) return; var c = Cesium.Cartographic.fromCartesian(cart); var lat = Cesium.Math.toDegrees(c.latitude); var lon = Cesium.Math.toDegrees(c.longitude); var best = null, bd = 999; for (var i = 0; i < self._nodeIndex.length; i++) { var el = self._nodeIndex[i]; var d = Math.abs(el.lat - lat) + Math.abs(el.lon - lon); if (d < bd) { bd = d; best = el; } } if (best && bd < 0.3) { var name = best.name || 'Unbenannt'; var typeLabels = { node: 'Node', way: 'Way', relation: 'Relation' }; var tagsHtml = ''; var tags = best.tags || {}; var tagKeys = Object.keys(tags); for (var t = 0; t < tagKeys.length && t < 20; t++) { var key = tagKeys[t]; tagsHtml += '