Vergiss verstopfte Nasen: Unser PHP Script Pollen Radar zeigt dir sofort, wie hoch die Belastung an deinem Ort wirklich ist. Mit klaren Tipps für den Alltag und zuverlässigen Daten, damit du weißt, wann du sicher lüften oder rausgehen kannst.
Den unten stehenden Code abspeichern als Setup.php, einmal aufrufen und alle einzel Dateien werden aus der Datei heraus erstellt. Der Installer kannst du im anschluss löschen.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
$dirs = ['assets/css', 'api/v1', 'includes/core'];
foreach ($dirs as $dir) { if (!is_dir($dir)) @mkdir($dir, 0755, true); }
$pollen_defs = [
'alder' => ['n' => 'Erle', 'c' => '#10b981'],
'birch' => ['n' => 'Birke', 'c' => '#34d399'],
'grass' => ['n' => 'Gräser', 'c' => '#fbbf24'],
'mugwort' => ['n' => 'Beifuss', 'c' => '#f87171'],
'olive' => ['n' => 'Olive', 'c' => '#60a5fa'],
'ragweed' => ['n' => 'Ambrosia', 'c' => '#a78bfa']
];
$config_php = "<?php
define('API_URL', 'https://air-quality-api.open-meteo.com/v1/air-quality');
\$POLLEN_DEFS = " . var_export($pollen_defs, true) . ";
\$CITIES = ['berlin' => ['n' => 'Berlin', 'lat' => 52.52, 'lon' => 13.41]];
?>";
file_put_contents('includes/core/config.php', $config_php);
$api_v1 = "<?php
header('Content-Type: application/json');
require_once __DIR__ . '/../../includes/core/config.php';
\$search = \$_GET['city'] ?? 'berlin';
\$lat = null; \$lon = null; \$foundName = ucfirst(\$search);
if (isset(\$CITIES[strtolower(\$search)])) {
\$lat = \$CITIES[strtolower(\$search)]['lat']; \$lon = \$CITIES[strtolower(\$search)]['lon'];
} else {
\$geoUrl = 'https://nominatim.openstreetmap.org/search?q=' . urlencode(\$search) . '&format=json&limit=1';
\$ch = curl_init(); curl_setopt(\$ch, CURLOPT_URL, \$geoUrl); curl_setopt(\$ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt(\$ch, CURLOPT_USERAGENT, 'PollenEliteApp/2.0');
\$geoRes = json_decode(curl_exec(\$ch), true); curl_close(\$ch);
if (!empty(\$geoRes)) { \$lat = \$geoRes[0]['lat']; \$lon = \$geoRes[0]['lon']; \$foundName = \$geoRes[0]['display_name']; }
}
if (\$lat && \$lon) {
\$pollenUrl = \"https://air-quality-api.open-meteo.com/v1/air-quality?latitude=\$lat&longitude=\$lon&hourly=alder_pollen,birch_pollen,grass_pollen,mugwort_pollen,olive_pollen,ragweed_pollen&timezone=auto\";
\$ch = curl_init(); curl_setopt(\$ch, CURLOPT_URL, \$pollenUrl); curl_setopt(\$ch, CURLOPT_RETURNTRANSFER, true);
\$res = curl_exec(\$ch); curl_close(\$ch);
\$data = json_decode(\$res, true); \$data['location_name'] = \$foundName; echo json_encode(\$data);
}
?>";
file_put_contents('api/v1/pollen.php', $api_v1);
$css = "
:root { --bg: #050807; --card: #0d1210; --accent: #10b981; --text: #f1f5f9; }
body { background: var(--bg); color: var(--text); font-family: 'Inter', sans-serif; margin: 0; min-height: 100vh; }
.glass { background: rgba(13, 18, 16, 0.9); backdrop-filter: blur(20px); border: 1px solid rgba(255,255,255,0.05); border-radius: 24px; }
header { padding: 1.5rem; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(16,185,129,0.1); }
.logo { font-size: 1.2rem; font-weight: 900; color: var(--accent); text-decoration: none; letter-spacing: 1px; }
.hero { padding: 3rem 1rem; text-align: center; background: radial-gradient(circle at center, rgba(16,185,129,0.08) 0%, transparent 70%); }
.search-container { display: flex; max-width: 500px; margin: 0 auto; gap: 10px; }
.search-input { flex: 1; padding: 1rem 1.5rem; border-radius: 50px; background: var(--card); border: 1px solid var(--accent); color: #fff; outline: none; }
.btn-search { background: var(--accent); border: none; border-radius: 50px; padding: 0 25px; color: #fff; cursor: pointer; font-weight: 700; transition: 0.3s; }
.btn-search:hover { transform: scale(1.05); box-shadow: 0 0 20px rgba(16,185,129,0.4); }
.results-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; padding: 2rem; max-width: 1400px; margin: 0 auto; }
.day-card { padding: 2rem; }
.pollen-label { font-size: 0.85rem; margin-bottom: 4px; display: flex; justify-content: space-between; }
.bar-bg { height: 8px; background: rgba(255,255,255,0.05); border-radius: 10px; margin-bottom: 15px; overflow: hidden; }
.bar-fill { height: 100%; transition: width 1s ease-in-out; }
.status-text { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; }
.advice-box { margin-top: 20px; padding: 10px; border-radius: 12px; background: rgba(255,255,255,0.03); font-size: 0.8rem; line-height: 1.4; opacity: 0.8; }
footer { margin-top: 50px; padding: 40px 20px; text-align: center; border-top: 1px solid rgba(16,185,129,0.1); background: rgba(13, 18, 16, 0.5); }
.footer-content { max-width: 1200px; margin: 0 auto; display: flex; flex-direction: column; align-items: center; gap: 15px; }
.dreamcodes-link { color: var(--accent); text-decoration: none; font-weight: 700; letter-spacing: 1px; transition: 0.3s; opacity: 0.8; }
.dreamcodes-link:hover { opacity: 1; text-shadow: 0 0 10px rgba(16,185,129,0.5); }
.copyright { font-size: 0.8rem; opacity: 0.4; }
";
file_put_contents('assets/css/elite.css', $css);
$index_php = "<?php require_once 'includes/core/config.php'; ?>
<!DOCTYPE html>
<html lang='de'>
<head>
<meta charset='UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Pollen Radar: Frei atmen durch Echtzeit Prognose</title>
<link rel='stylesheet' href='assets/css/elite.css'>
</head>
<body>
<header class='glass'><a href='index.php' class='logo'>Pollen.Radar</a><div id='status'>Online</div></header>
<section class='hero'>
<h1>Wie ist die <span style='color:var(--accent)'>Belastung?</span></h1>
<input type='text' id='citySearch' class='search-input' placeholder='Ort eingeben...' onkeypress='if(event.key===\"Enter\") load(this.value)'>
</section>
<main class='results-grid' id='app'></main>
<script>
const DEFS = " . json_encode($pollen_defs) . ";
function getLevel(v) {
if (v <= 0.5) return {
t: 'Keine Belastung',
c: '#10b981',
a: 'Ideal für Outdoor-Aktivitäten. Lüften ist uneingeschränkt möglich.'
};
if (v <= 2.0) return {
t: 'Gering',
c: '#34d399',
a: 'Kaum Symptome. Empfindliche Personen sollten nach dem Spaziergang die Haare waschen.'
};
if (v <= 10.0) return {
t: 'Mittel',
c: '#fbbf24',
a: 'Leichte Symptome möglich. Lüften Sie am besten nur stoßweise bei geringem Wind.'
};
if (v <= 30.0) return {
t: 'Hoch',
c: '#f87171',
a: 'Starke Belastung! Kleidung nicht im Schlafzimmer ausziehen. Fenster nachts geschlossen halten.'
};
return {
t: 'Sehr Hoch',
c: '#ef4444',
a: 'Extreme Belastung! Aufenthalt im Freien vermeiden. Nutzen Sie Luftreiniger mit HEPA-Filter.'
};
}
async function load(query) {
const app = document.getElementById('app');
app.style.opacity = '0.3';
const r = await fetch('api/v1/pollen.php?city=' + encodeURIComponent(query));
const d = await r.json();
app.innerHTML = ''; app.style.opacity = '1';
document.getElementById('status').innerText = d.location_name;
for(let i=0; i<4; i++) {
const dt = new Date(); dt.setDate(dt.getDate() + i);
let html = '<div class=\"day-card glass\"><h3>' + dt.toLocaleDateString('de-DE',{weekday:'long'}) + '</h3><br>';
let maxImpact = 0;
Object.keys(DEFS).forEach(p => {
const raw = d.hourly[p + \"_pollen\"][i * 24 + 12];
const v = raw || 0;
if(v > maxImpact) maxImpact = v;
const level = getLevel(v);
const pct = Math.min(100, (v / 30) * 100);
html += `<div class=\"pollen-label\"><span>\${DEFS[p].n}</span><span class=\"status-text\" style=\"color:\${level.c}\">\${level.t}</span></div>
<div class=\"bar-bg\"><div class=\"bar-fill\" style=\"width:\${pct}%; background:\${level.c}\"></div></div>`;
});
const dayAdvice = getLevel(maxImpact);
html += `<div class=\"advice-box\"><strong>Tipp:</strong> \${dayAdvice.a}</div></div>`;
app.innerHTML += html;
}
}
load('Berlin');
</script>
<footer class="glass">
<div class="footer-content">
<a href="https://www.dreamcodes.net" target="_blank" class="dreamcodes-link">
POWERED BY DREAMCODES
</a>
<div class="copyright">
© <?php echo date('Y'); ?> Pollen-Radar
</div>
</div>
</footer>
</body>
</html>";
file_put_contents('index.php', $index_php);
echo "<h1>Installation abgeschlossen.</h1><p><a href='index.php'>ZUM DASHBOARD</a></p>";
