{"id":3205,"date":"2026-04-05T09:31:33","date_gmt":"2026-04-05T08:31:33","guid":{"rendered":"https:\/\/hb9id.ch\/?page_id=3205"},"modified":"2026-04-05T18:36:21","modified_gmt":"2026-04-05T17:36:21","slug":"sichtfeldrechner","status":"publish","type":"page","link":"https:\/\/hb9id.ch\/index.php\/innovationen\/sichtfeldrechner\/","title":{"rendered":"Sichtfeldrechner"},"content":{"rendered":"    <div id=\"hb9id-plugin-container\">\n        <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Figtree:wght@400;600;700&display=swap\" rel=\"stylesheet\">\n        <link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\" \/>\n        <script src=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\"><\/script>\n\n        <style>\n            #hb9id-sichtfeldrechner-app {\n                font-family: 'Figtree', sans-serif;\n                color: #333;\n                background: #fff;\n                padding: 15px;\n                box-sizing: border-box;\n                max-width: 1200px;\n                margin: 0 auto;\n                line-height: 1.4;\n            }\n\n            #hb9id-sichtfeldrechner-app h3 { \n                font-size: 20px; margin: 0 0 10px 0; font-weight: 700; color: #333; \n            }\n            \n            .hb9id-controls-top {\n                background-color: #f9f9f9;\n                padding: 10px 15px;\n                border-radius: 8px;\n                border: 1px solid #eee;\n                margin-bottom: 10px;\n                display: flex;\n                flex-direction: column;\n                gap: 10px;\n            }\n\n            .hb9id-input-row {\n                display: grid;\n                grid-template-columns: 2.5fr 1fr 1fr 1fr 1fr;\n                gap: 10px;\n                align-items: end;\n            }\n\n            .hb9id-input-group { \n                display: flex; flex-direction: column; justify-content: flex-end; \n            }\n            .hb9id-input-group label { \n                font-size: 11px; font-weight: 600; color: #555; margin-bottom: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n            }\n            \n            #hb9id-sichtfeldrechner-app input[type=\"text\"], \n            #hb9id-sichtfeldrechner-app input[type=\"number\"] {\n                font-family: 'Figtree', sans-serif; \n                font-size: 13px; \n                padding: 6px 8px; \n                border: 1px solid #ccc; \n                border-radius: 4px; \n                width: 100%; \n                box-sizing: border-box;\n                background: #fff;\n                color: #333;\n                height: 32px;\n                box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);\n            }\n\n            #hb9id-sichtfeldrechner-app input[type=\"text\"]:focus, \n            #hb9id-sichtfeldrechner-app input[type=\"number\"]:focus {\n                border-color: #DD9933; outline: none; box-shadow: 0 0 0 2px rgba(221, 153, 51, 0.2);\n            }\n\n            #hb9id-sichtfeldrechner-app input[readonly] {\n                background: #f4f4f4; color: #666; cursor: not-allowed;\n            }\n\n            .hb9id-action-row {\n                display: flex;\n                align-items: center;\n                justify-content: space-between;\n                border-top: 1px solid #e5e5e5;\n                padding-top: 10px;\n            }\n\n            .hb9id-toggles-container {\n                display: flex;\n                gap: 20px;\n            }\n\n            .hb9id-switch-wrapper { display: flex; align-items: center; gap: 8px; }\n            .hb9id-switch-wrapper label { margin-bottom: 0; cursor: pointer; font-size: 12px; font-weight: 600; color: #444; }\n            .hb9id-switch { position: relative; display: inline-block; width: 32px; height: 18px; }\n            .hb9id-switch input { opacity: 0; width: 0; height: 0; }\n            .hb9id-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 18px; }\n            .hb9id-slider:before { position: absolute; content: \"\"; height: 12px; width: 12px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }\n            input:checked + .hb9id-slider { background-color: #DD9933; }\n            input:checked + .hb9id-slider:before { transform: translateX(14px); }\n\n            .hb9id-btn-group {\n                display: flex; gap: 8px; justify-content: flex-end;\n            }\n\n            #hb9id-sichtfeldrechner-app button { \n                padding: 6px 16px; background: #DD9933; color: white; border: none; border-radius: 4px; \n                cursor: pointer; font-weight: 700; font-size: 13px; height: 32px; transition: background 0.2s;\n            }\n            #hb9id-sichtfeldrechner-app button:hover { background: #c5862b; }\n            #hb9id-sichtfeldrechner-app .hb9id-btn-secondary { background: #555; }\n            #hb9id-sichtfeldrechner-app .hb9id-btn-secondary:hover { background: #333; }\n\n            .hb9id-map-wrapper { position: relative; border-radius: 6px; overflow: hidden; border: 1px solid #ddd; height: 600px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); display: flex; flex-direction: column; }\n            #hb9id-map { flex-grow: 1; width: 100%; z-index: 1; background: #f8f8f8; }\n            \n            .hb9id-bw-mode #hb9id-map .leaflet-tile-container {\n                filter: grayscale(100%);\n            }\n\n            .hb9id-loading-overlay {\n                position: absolute; top: 0; left: 0; width: 100%; height: 100%;\n                background: rgba(255, 255, 255, 0.85); display: none; flex-direction: column; \n                justify-content: center; align-items: center; z-index: 1000; backdrop-filter: blur(4px);\n            }\n            .hb9id-progress-container { width: 60%; background: #eee; border-radius: 20px; height: 12px; overflow: hidden; border: 1px solid #ddd; }\n            \n            #hb9id-progress-bar { \n                width: 0%; height: 100%; background-color: #DD9933; \n                background-image: linear-gradient(45deg, rgba(255,255,255,0.25) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.25) 50%, rgba(255,255,255,0.25) 75%, transparent 75%, transparent);\n                background-size: 1rem 1rem;\n                animation: hb9id-progress-stripes 1s linear infinite;\n                transition: width 0.1s linear; \n            }\n            @keyframes hb9id-progress-stripes { from { background-position: 1rem 0; } to { background-position: 0 0; } }\n\n            #hb9id-status-text { margin-top: 12px; font-weight: 600; font-size: 13px; color: #222; }\n            #hb9id-print-header, #hb9id-print-footer { display: none; }\n            .hb9id-grid-label { color: #DD9933; font-weight: bold; font-family: monospace; font-size: 13px; text-shadow: 1px 1px 0 #fff, -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 0px 0px 3px rgba(255,255,255,0.8); text-align: center; pointer-events: none; }\n\n            @media print {\n                @page { size: A4 landscape; margin: 0; }\n                html, body { width: 100% !important; height: 100% !important; margin: 0 !important; padding: 0 !important; background: white !important; }\n                body.hb9id-print-mode > * { display: none !important; }\n                body.hb9id-print-mode > #hb9id-plugin-container { display: block !important; height: 100%; }\n                #hb9id-sichtfeldrechner-app { width: 297mm !important; height: 210mm !important; margin: 0 !important; padding: 10mm !important; box-sizing: border-box !important; display: flex !important; flex-direction: column !important; background: white !important; }\n                #hb9id-sichtfeldrechner-app .hb9id-controls-top, #hb9id-sichtfeldrechner-app h3 { display: none !important; }\n                #hb9id-print-header { display: flex !important; justify-content: space-between !important; border-bottom: 2px solid #DD9933 !important; padding-bottom: 6px !important; margin-bottom: 10px !important; }\n                .hb9id-map-wrapper { flex-grow: 1 !important; border: 1px solid #000 !important; box-shadow: none !important; }\n                #hb9id-print-footer { display: block !important; text-align: center !important; font-size: 10px !important; color: #666 !important; padding-top: 6px !important; margin-top: 8px !important; border-top: 1px solid #DD9933 !important; }\n            }\n\n            @media (max-width: 850px) { \n                .hb9id-input-row { grid-template-columns: 1fr 1fr; }\n                .hb9id-input-group:first-child { grid-column: span 2; }\n                .hb9id-map-wrapper { height: 450px; } \n                .hb9id-action-row { flex-direction: column; gap: 15px; align-items: flex-start; }\n                .hb9id-toggles-container { flex-direction: column; gap: 10px; }\n                .hb9id-btn-group { width: 100%; justify-content: space-between; }\n                .hb9id-btn-group button { width: 48%; }\n            }\n        <\/style>\n\n        <div id=\"hb9id-sichtfeldrechner-app\">\n            <h3>HB9ID Sichtfeldrechner<\/h3>\n\n            <div id=\"hb9id-print-header\">\n                <div><h2>HB9ID Sichtfeldrechner<\/h2><\/div>\n                <div class=\"hb9id-print-data\">\n                    <strong>Erstellt am:<\/strong> <span id=\"hb9id-print-date\">-<\/span><br>\n                    <strong>Locator:<\/strong> <span id=\"hb9id-print-loc\">-<\/span> &nbsp;|&nbsp;\n                    <strong>Koordinaten:<\/strong> <span id=\"hb9id-print-coord\">-<\/span><br>\n                    <strong>H\u00f6he:<\/strong> <span id=\"hb9id-print-base\">-<\/span>m &nbsp;|&nbsp;\n                    <strong>Antenne:<\/strong> <span id=\"hb9id-print-ant\">-<\/span>m &nbsp;|&nbsp;\n                    <strong>Radius:<\/strong> <span id=\"hb9id-print-rad\">-<\/span>km\n                <\/div>\n            <\/div>\n\n            <div class=\"hb9id-controls-top\">\n                <div class=\"hb9id-input-row\">\n                    <div class=\"hb9id-input-group\">\n                        <label>Koordinaten oder Ort suchen<\/label>\n                        <div style=\"display: flex; gap: 4px;\">\n                            <input type=\"text\" id=\"hb9id-coords\" placeholder=\"z.B. 47.38848, 8.97894\">\n                            <button type=\"button\" id=\"hb9id-locate-btn\" class=\"hb9id-btn-secondary\" onclick=\"hb9idLocateMe()\" title=\"Standort verwenden\" style=\"width: 32px; padding: 0; display: flex; align-items: center; justify-content: center; margin: 0; flex-shrink: 0;\">\ud83d\udccd<\/button>\n                        <\/div>\n                    <\/div>\n                    <div class=\"hb9id-input-group\">\n                        <label>Locator<\/label>\n                        <input type=\"text\" id=\"hb9id-locator\" value=\"------\" maxlength=\"6\">\n                    <\/div>\n                    <div class=\"hb9id-input-group\">\n                        <label>Bodenh\u00f6he (m.\u00fc.M)<\/label>\n                        <input type=\"number\" id=\"hb9id-baseHeight\" readonly>\n                    <\/div>\n                    <div class=\"hb9id-input-group\">\n                        <label>Antenne (m)<\/label>\n                        <input type=\"number\" id=\"hb9id-antHeight\" value=\"10\">\n                    <\/div>\n                    <div class=\"hb9id-input-group\">\n                        <label>Radius (km)<\/label>\n                        <input type=\"number\" id=\"hb9id-radiusKM\" value=\"5\" min=\"1\" max=\"120\">\n                    <\/div>\n                <\/div>\n\n                <div class=\"hb9id-action-row\">\n                    <div class=\"hb9id-toggles-container\">\n                        <div class=\"hb9id-switch-wrapper\">\n                            <label class=\"hb9id-switch\">\n                                <input type=\"checkbox\" id=\"hb9id-grid-toggle\" checked>\n                                <span class=\"hb9id-slider\"><\/span>\n                            <\/label>\n                            <label for=\"hb9id-grid-toggle\">Locator Grid<\/label>\n                        <\/div>\n                        <div class=\"hb9id-switch-wrapper\">\n                            <label class=\"hb9id-switch\">\n                                <input type=\"checkbox\" id=\"hb9id-res-toggle\">\n                                <span class=\"hb9id-slider\"><\/span>\n                            <\/label>\n                            <label for=\"hb9id-res-toggle\">Grob (>30km)<\/label>\n                        <\/div>\n                        <div class=\"hb9id-switch-wrapper\">\n                            <label class=\"hb9id-switch\">\n                                <input type=\"checkbox\" id=\"hb9id-bw-toggle\" checked>\n                                <span class=\"hb9id-slider\"><\/span>\n                            <\/label>\n                            <label for=\"hb9id-bw-toggle\">Schwarz\/Weiss<\/label>\n                        <\/div>\n                    <\/div>\n                    <div class=\"hb9id-btn-group\">\n                        <button class=\"hb9id-btn-secondary\" onclick=\"hb9idPrintPDF()\">Als PDF speichern<\/button>\n                        <button onclick=\"hb9idCalculateLOS()\">Berechnung starten<\/button>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <div class=\"hb9id-map-wrapper hb9id-bw-mode\" id=\"hb9id-map-frame\">\n                <div id=\"hb9id-loading-overlay\" class=\"hb9id-loading-overlay\">\n                    <div class=\"hb9id-progress-container\"><div id=\"hb9id-progress-bar\"><\/div><\/div>\n                    <div id=\"hb9id-status-text\">Lade echte Topografie-Daten...<\/div>\n                <\/div>\n                <div id=\"hb9id-map\"><\/div>\n            <\/div>\n\n            <div id=\"hb9id-print-footer\">\n                Generiert durch den HB9ID Funkverein Iddaburg \u2013 www.hb9id.ch\n            <\/div>\n        <\/div>\n\n        <script>\n        document.addEventListener(\"DOMContentLoaded\", function() {\n            const startLat = 47.38848;\n            const startLng = 8.97894;\n            const map = L.map('hb9id-map').setView([startLat, startLng], 14);\n            \n            L.tileLayer('https:\/\/{s}.tile.opentopomap.org\/{z}\/{x}\/{y}.png', {\n                maxZoom: 17,\n                attribution: 'Kartendaten: &copy; OSM, SRTM | Map: &copy; OpenTopoMap (CC-BY-SA)'\n            }).addTo(map);\n\n            let gridLayer = L.layerGroup().addTo(map);\n            let locatorGridLayer = L.layerGroup().addTo(map);\n            let marker = L.marker([startLat, startLng], {draggable: true}).addTo(map);\n\n            document.getElementById('hb9id-bw-toggle').addEventListener('change', function(e) {\n                const frame = document.getElementById('hb9id-map-frame');\n                if(e.target.checked) frame.classList.add('hb9id-bw-mode');\n                else frame.classList.remove('hb9id-bw-mode');\n            });\n\n            \/\/ Automatischer Wechsel auf grobes Raster bei Radius > 30km\n            document.getElementById('hb9id-radiusKM').addEventListener('input', function(e) {\n                const radius = parseFloat(e.target.value);\n                const resToggle = document.getElementById('hb9id-res-toggle');\n                if (radius > 30) {\n                    resToggle.checked = true;\n                } else if (radius <= 30 && radius > 0) {\n                    resToggle.checked = false;\n                }\n            });\n\n            function getLocator(lat, lon) {\n                let lon_adj = lon + 180; let lat_adj = lat + 90;\n                let l1 = String.fromCharCode(65 + Math.floor(lon_adj \/ 20));\n                let l2 = String.fromCharCode(65 + Math.floor(lat_adj \/ 10));\n                let l3 = Math.floor((lon_adj % 20) \/ 2);\n                let l4 = Math.floor(lat_adj % 10);\n                let l5 = String.fromCharCode(97 + Math.floor((lon_adj % 2) * 12));\n                let l6 = String.fromCharCode(97 + Math.floor((lat_adj % 1) * 24));\n                return (l1 + l2 + l3 + l4 + l5 + l6).toUpperCase();\n            }\n\n            function locatorToLatLng(loc) {\n                loc = loc.toUpperCase().trim();\n                if (!\/^[A-R]{2}[0-9]{2}[A-X]{2}$\/.test(loc) && !\/^[A-R]{2}[0-9]{2}$\/.test(loc)) return null;\n                let lon = (loc.charCodeAt(0) - 65) * 20 - 180;\n                let lat = (loc.charCodeAt(1) - 65) * 10 - 90;\n                if (loc.length >= 4) { lon += parseInt(loc.charAt(2)) * 2; lat += parseInt(loc.charAt(3)) * 1; }\n                if (loc.length === 6) { lon += (loc.charCodeAt(4) - 65) * (2\/24) + (1\/24); lat += (loc.charCodeAt(5) - 65) * (1\/24) + (0.5\/24); }\n                else if (loc.length === 4) { lon += 1; lat += 0.5; }\n                return { lat: lat, lng: lon };\n            }\n\n            function drawLocatorGrid() {\n                locatorGridLayer.clearLayers();\n                if (!document.getElementById('hb9id-grid-toggle').checked) return;\n                let bounds = map.getBounds();\n                let zoom = map.getZoom();\n                let isHighRes = zoom > 8; \n                let latStep = isHighRes ? (1\/24) : 1; \n                let lonStep = isHighRes ? (2\/24) : 2; \n                let minLat = Math.floor(bounds.getSouth() \/ latStep) * latStep;\n                let maxLat = Math.ceil(bounds.getNorth() \/ latStep) * latStep;\n                let minLon = Math.floor(bounds.getWest() \/ lonStep) * lonStep;\n                let maxLon = Math.ceil(bounds.getEast() \/ lonStep) * lonStep;\n                for (let lat = minLat; lat < maxLat; lat += latStep) {\n                    for (let lon = minLon; lon < maxLon; lon += lonStep) {\n                        L.rectangle([[lat, lon], [lat + latStep, lon + lonStep]], { color: '#DD9933', weight: 1.5, fill: false, opacity: 0.6, interactive: false }).addTo(locatorGridLayer);\n                        let centerLat = lat + latStep\/2;\n                        let centerLon = lon + lonStep\/2;\n                        let loc = getLocator(centerLat, centerLon);\n                        let labelText = isHighRes ? loc : loc.substring(0,4);\n                        let icon = L.divIcon({ className: 'hb9id-grid-label', html: `<div>${labelText}<\/div>`, iconSize: [60, 20], iconAnchor: [30, 10] });\n                        L.marker([centerLat, centerLon], {icon: icon, interactive: false}).addTo(locatorGridLayer);\n                    }\n                }\n            }\n            map.on('moveend', drawLocatorGrid);\n            document.getElementById('hb9id-grid-toggle').addEventListener('change', drawLocatorGrid);\n\n            function parseCoordinates(val) {\n                val = val.trim();\n                let dec = val.match(\/([-+]?\\d{1,3}\\.\\d+)[,\\s]+([-+]?\\d{1,3}\\.\\d+)\/);\n                if(dec) return { lat: parseFloat(dec[1]), lng: parseFloat(dec[2]) };\n                let dms = val.match(\/(\\d+)[^\\d]+(\\d+)[^\\d]+([\\d.]+)[^\\dNS]*([NS])[^\\d]*(\\d+)[^\\d]+(\\d+)[^\\d]+([\\d.]+)[^\\dEOW]*([EOW])\/i);\n                if(dms) {\n                    let lat = parseFloat(dms[1]) + parseFloat(dms[2])\/60 + parseFloat(dms[3])\/3600;\n                    if(dms[4].toUpperCase() === 'S') lat *= -1;\n                    let lng = parseFloat(dms[5]) + parseFloat(dms[6])\/60 + parseFloat(dms[7])\/3600;\n                    if(dms[8].toUpperCase() === 'W') lng *= -1;\n                    return { lat: lat, lng: lng };\n                }\n                return null;\n            }\n\n            const tileCache = {};\n            function getTileCoords(lat, lng, z) {\n                const x = Math.floor((lng + 180) \/ 360 * Math.pow(2, z));\n                const y = Math.floor((1 - Math.log(Math.tan(lat * Math.PI \/ 180) + 1 \/ Math.cos(lat * Math.PI \/ 180)) \/ Math.PI) \/ 2 * Math.pow(2, z));\n                return {x, y, z};\n            }\n\n            async function loadTile(x, y, z) {\n                const key = `${z}_${x}_${y}`;\n                if (tileCache[key]) return;\n                return new Promise((resolve) => {\n                    const img = new Image(); img.crossOrigin = \"anonymous\";\n                    img.src = `https:\/\/s3.amazonaws.com\/elevation-tiles-prod\/terrarium\/${z}\/${x}\/${y}.png`;\n                    img.onload = () => {\n                        const canvas = document.createElement('canvas'); canvas.width = 256; canvas.height = 256;\n                        const ctx = canvas.getContext('2d', { willReadFrequently: true });\n                        ctx.drawImage(img, 0, 0); tileCache[key] = ctx.getImageData(0, 0, 256, 256).data; resolve();\n                    };\n                    img.onerror = () => {\n                        const empty = new Uint8ClampedArray(256*256*4); for(let i=0; i<empty.length; i+=4) { empty[i]=128; empty[i+1]=0; empty[i+2]=0; empty[i+3]=255; }\n                        tileCache[key] = empty; resolve();\n                    };\n                });\n            }\n\n            function getElevation(lat, lng, z) {\n                const n = Math.pow(2, z);\n                const x_exact = (lng + 180) \/ 360 * n;\n                const y_exact = (1 - Math.log(Math.tan(lat * Math.PI \/ 180) + 1 \/ Math.cos(lat * Math.PI \/ 180)) \/ Math.PI) \/ 2 * n;\n                const tx = Math.floor(x_exact); const ty = Math.floor(y_exact);\n                const data = tileCache[`${z}_${tx}_${ty}`]; if (!data) return 0;\n                const px = Math.floor((x_exact - tx) * 256); const py = Math.floor((y_exact - ty) * 256);\n                const idx = (py * 256 + px) * 4;\n                return (data[idx] * 256 + data[idx+1] + data[idx+2] \/ 256) - 32768;\n            }\n\n            function getCurvatureDrop(d) { return (d * d) \/ 12742000; }\n\n            async function updatePos(lat, lng, source) {\n                if(source !== 'input') document.getElementById('hb9id-coords').value = `${lat.toFixed(5)}, ${lng.toFixed(5)}`;\n                if(source !== 'locator') document.getElementById('hb9id-locator').value = getLocator(lat, lng);\n                marker.setLatLng([lat, lng]);\n                const t = getTileCoords(lat, lng, 13);\n                await loadTile(t.x, t.y, 13);\n                document.getElementById('hb9id-baseHeight').value = Math.round(getElevation(lat, lng, 13));\n            }\n\n            marker.on('dragend', (e) => updatePos(e.target.getLatLng().lat, e.target.getLatLng().lng));\n            map.on('click', (e) => updatePos(e.latlng.lat, e.latlng.lng));\n\n            document.getElementById('hb9id-coords').addEventListener('change', async (e) => {\n                const inputVal = e.target.value; const coords = parseCoordinates(inputVal);\n                if(coords) { map.setView([coords.lat, coords.lng], map.getZoom()); updatePos(coords.lat, coords.lng, 'input'); }\n                else if(inputVal.trim().length > 2) {\n                    try {\n                        const response = await fetch(`https:\/\/nominatim.openstreetmap.org\/search?format=json&q=${encodeURIComponent(inputVal)}&limit=1`);\n                        const data = await response.json();\n                        if(data && data.length > 0) { const lat = parseFloat(data[0].lat); const lon = parseFloat(data[0].lon); map.setView([lat, lon], 13); updatePos(lat, lon, 'input'); }\n                    } catch (err) { console.error(\"Ortssuche fehlgeschlagen\", err); }\n                }\n            });\n\n            document.getElementById('hb9id-locator').addEventListener('change', (e) => {\n                const coords = locatorToLatLng(e.target.value);\n                if(coords) { map.setView([coords.lat, coords.lng], map.getZoom()); updatePos(coords.lat, coords.lng, 'locator'); }\n            });\n\n            window.hb9idLocateMe = function() {\n                if (\"geolocation\" in navigator) {\n                    navigator.geolocation.getCurrentPosition(function(position) {\n                        const lat = position.coords.latitude; const lng = position.coords.longitude;\n                        map.setView([lat, lng], 13); updatePos(lat, lng, 'locate');\n                    });\n                }\n            };\n\n            window.hb9idCalculateLOS = async function() {\n                const overlay = document.getElementById('hb9id-loading-overlay');\n                const bar = document.getElementById('hb9id-progress-bar');\n                const status = document.getElementById('hb9id-status-text');\n                const pos = marker.getLatLng();\n                const antH = parseFloat(document.getElementById('hb9id-antHeight').value);\n                let radiusKM = parseFloat(document.getElementById('hb9id-radiusKM').value);\n                \n                \/\/ Falls Radius > 30, sicherstellen dass isCoarse true ist (doppelte Absicherung)\n                const resToggle = document.getElementById('hb9id-res-toggle');\n                if(radiusKM > 30) resToggle.checked = true;\n                \n                const isCoarse = resToggle.checked;\n                const workZoom = isCoarse ? 10 : 13;\n                const stepKM = isCoarse ? 0.75 : 0.15;\n                const rayStepM = isCoarse ? 250 : 50;\n                \n                overlay.style.display = 'flex';\n                gridLayer.clearLayers(); marker.addTo(gridLayer);\n                status.innerText = \"Lade DEM Kacheln...\"; bar.style.width = \"10%\";\n\n                const latMin = pos.lat - (radiusKM \/ 111.32); const latMax = pos.lat + (radiusKM \/ 111.32);\n                const lngMin = pos.lng - (radiusKM \/ (111.32 * Math.cos(pos.lat * Math.PI \/ 180)));\n                const lngMax = pos.lng + (radiusKM \/ (111.32 * Math.cos(pos.lat * Math.PI \/ 180)));\n                const tl = getTileCoords(latMax, lngMin, workZoom); const br = getTileCoords(latMin, lngMax, workZoom);\n                const tilePromises = [];\n                for (let x = tl.x; x <= br.x; x++) { for (let y = tl.y; y <= br.y; y++) { tilePromises.push(loadTile(x, y, workZoom)); } }\n                await Promise.all(tilePromises);\n\n                const latStep = (stepKM \/ 111.32); const lngStep = (stepKM \/ (111.32 * Math.cos(pos.lat * Math.PI \/ 180)));\n                const steps = Math.ceil(radiusKM \/ stepKM);\n                let points = [];\n                for (let i = -steps; i <= steps; i++) { for (let j = -steps; j <= steps; j++) { if (Math.sqrt(i*i + j*j) <= steps) points.push({i, j}); } }\n                const startBaseH = getElevation(pos.lat, pos.lng, workZoom); const startTotalH = startBaseH + antH;\n\n                for (let k = 0; k < points.length; k++) {\n                    const {i, j} = points[k]; const targetLat = pos.lat + (i * latStep); const targetLon = pos.lng + (j * lngStep);\n                    const totalDistM = Math.sqrt(i*i + j*j) * stepKM * 1000;\n                    let isVisible = true;\n                    if (totalDistM > 0) {\n                        const targetZ = getElevation(targetLat, targetLon, workZoom);\n                        const targetAngle = Math.atan2((targetZ - getCurvatureDrop(totalDistM)) - startTotalH, totalDistM);\n                        const numSteps = Math.max(2, Math.ceil(totalDistM \/ rayStepM));\n                        for (let s = 1; s < numSteps; s++) {\n                            const fraction = s \/ numSteps; const d = totalDistM * fraction;\n                            const checkLat = pos.lat + (i * latStep * fraction); const checkLon = pos.lng + (j * lngStep * fraction);\n                            const checkZ = getElevation(checkLat, checkLon, workZoom);\n                            const checkAngle = Math.atan2((checkZ - getCurvatureDrop(d)) - startTotalH, d);\n                            if (checkAngle >= targetAngle) { isVisible = false; break; }\n                        }\n                    }\n                    L.rectangle([[targetLat - latStep\/2, targetLon - lngStep\/2], [targetLat + latStep\/2, targetLon + lngStep\/2]], {\n                        color: isVisible ? \"#77dd77\" : \"transparent\", fillColor: isVisible ? \"#77dd77\" : \"#cccccc\",\n                        weight: isVisible ? (isCoarse ? 0.3 : 0.5) : 0, fillOpacity: isVisible ? 0.4 : 0.2, interactive: false\n                    }).addTo(gridLayer);\n                    if (k % 150 === 0) { bar.style.width = 20 + ((k \/ points.length) * 80) + \"%\"; await new Promise(r => setTimeout(r, 0)); }\n                }\n                overlay.style.display = 'none';\n                map.fitBounds([[latMin, lngMin], [latMax, lngMax]], { padding: [1, 1] });\n            };\n\n            window.hb9idPrintPDF = function() {\n                const now = new Date();\n                const locVal = document.getElementById('hb9id-locator').value || 'Unbekannt';\n                document.getElementById('hb9id-print-date').innerText = now.toLocaleString('de-CH');\n                document.getElementById('hb9id-print-loc').innerText = locVal;\n                document.getElementById('hb9id-print-coord').innerText = document.getElementById('hb9id-coords').value;\n                document.getElementById('hb9id-print-base').innerText = document.getElementById('hb9id-baseHeight').value;\n                document.getElementById('hb9id-print-ant').innerText = document.getElementById('hb9id-antHeight').value;\n                document.getElementById('hb9id-print-rad').innerText = document.getElementById('hb9id-radiusKM').value;\n                const container = document.getElementById('hb9id-plugin-container');\n                const placeholder = document.createElement('div'); placeholder.id = 'hb9id-placeholder';\n                container.parentNode.insertBefore(placeholder, container);\n                document.body.appendChild(container); document.body.classList.add('hb9id-print-mode');\n                setTimeout(() => { map.invalidateSize(); setTimeout(() => { window.print(); document.body.classList.remove('hb9id-print-mode'); placeholder.parentNode.insertBefore(container, placeholder); placeholder.remove(); }, 800); }, 100);\n            };\n\n            updatePos(startLat, startLng);\n            drawLocatorGrid();\n        });\n        <\/script>\n    <\/div>\n    \n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"has-text-align-center\">Findest du unsere L\u00f6sungen n\u00fctzlich? Dann unterst\u00fctze uns mit einer Spende!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"350\" src=\"https:\/\/hb9id.ch\/wp-content\/uploads\/2026\/03\/TWINT_Individueller-Betrag_DE-768x350-1.png\" alt=\"\" class=\"wp-image-3053\" srcset=\"https:\/\/hb9id.ch\/wp-content\/uploads\/2026\/03\/TWINT_Individueller-Betrag_DE-768x350-1.png 768w, https:\/\/hb9id.ch\/wp-content\/uploads\/2026\/03\/TWINT_Individueller-Betrag_DE-768x350-1-300x137.png 300w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/figure>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Findest du unsere L\u00f6sungen n\u00fctzlich? Dann unterst\u00fctze uns mit einer Spende!<\/p>\n","protected":false},"author":8,"featured_media":0,"parent":2953,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-3205","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/pages\/3205","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/users\/8"}],"replies":[{"embeddable":true,"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/comments?post=3205"}],"version-history":[{"count":2,"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/pages\/3205\/revisions"}],"predecessor-version":[{"id":3208,"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/pages\/3205\/revisions\/3208"}],"up":[{"embeddable":true,"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/pages\/2953"}],"wp:attachment":[{"href":"https:\/\/hb9id.ch\/index.php\/wp-json\/wp\/v2\/media?parent=3205"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}