10 km) * * Sicherheitsnetz: * Δ < 0.3 km → keep (zu nah, Ortszentrum-Toleranz) * 0.3 km ≤ Δ < 10 km → auto-apply (safe) * Δ ≥ 10 km → suspicious (nur manuell per Flag) * OSM nichts gefunden → keep + flag * * Targets (fiktive Einsatzorte) nutzen hart fixierte Koords, keine Suche. */ require_once __DIR__ . '/config/app.php'; require_once __DIR__ . '/config/db.php'; set_time_limit(600); $mode = $_GET['mode'] ?? 'preview'; $confirm = $_GET['confirm'] ?? ''; $includeSusp = !empty($_GET['include_susp']); // --------------------------------------------------------------- // Konfiguration: feste Queries pro key, wo Nominatim vom Namen // allein falsch greifen würde // --------------------------------------------------------------- $OVERRIDE_QUERY = [ 'nenzing' => 'Christophorus 8 Nenzing', 'hohenems' => 'Flugplatz Hohenems-Dornbirn', 'innsbruck' => 'Flughafen Innsbruck', 'zams_c5' => 'Krankenhaus St. Vinzenz Zams', 'kitzbuehel_c4' => 'Christophorus 4 Reith', 'lienz_c7' => 'Bezirkskrankenhaus Lienz', 'brenner' => 'Brennerpass', // OSM-Fehltreffer vermeiden: 'oetz' => 'Oetz, Ötztal, Tirol', 'stuben' => 'Stuben am Arlberg', ]; // Manuell gesetzte Koords für fiktive Einsatzorte (targets) $TARGET_COORDS = [ 'bergrettung_muels' => [47.29325, 9.88957, 'Uga-Express Damüls (Talstation Skigebiet)'], 'skiunfall_soelden' => [46.97564, 10.97486, 'Giggijochbahn Sölden (Skigebiet)'], 'verkehr_a14' => [47.27063, 9.62238, 'A14 Rheintalautobahn bei Rankweil/Brederis'], ]; // --------------------------------------------------------------- function haversine_km($lat1, $lon1, $lat2, $lon2) { $R = 6371.0; $dLat = deg2rad($lat2 - $lat1); $dLon = deg2rad($lon2 - $lon1); $a = sin($dLat/2) ** 2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon/2) ** 2; return $R * 2 * atan2(sqrt($a), sqrt(1 - $a)); } function clean_name($name) { $n = preg_replace('/^C\d+\s+/', '', $name); $n = preg_replace('/\s*\(.*?\)\s*/', '', $n); return trim($n); } function nominatim_lookup($query) { $url = 'https://nominatim.openstreetmap.org/search?' . http_build_query(['q' => $query, 'format' => 'json', 'countrycodes' => 'at', 'limit' => 1]); $ctx = stream_context_create(['http' => [ 'header' => "User-Agent: GeoGraSim-Waypoint-Refresh/1.0 (lokal, Bildungsprojekt)\r\n", 'timeout' => 10, ]]); $resp = @file_get_contents($url, false, $ctx); if ($resp === false) return null; $j = json_decode($resp, true); if (!is_array($j) || count($j) === 0) return null; return ['lat' => (float)$j[0]['lat'], 'lon' => (float)$j[0]['lon'], 'display' => $j[0]['display_name']]; } // --------------------------------------------------------------- $db = getDB(); $rows = $db->query("SELECT wp_key, name, lat, lon, region, wp_type FROM geo_waypoints ORDER BY wp_type, region, name")->fetchAll(); $proposals = []; foreach ($rows as $w) { $p = [ 'wp_key' => $w['wp_key'], 'name' => $w['name'], 'region' => $w['region'], 'wp_type' => $w['wp_type'], 'old_lat' => (float)$w['lat'], 'old_lon' => (float)$w['lon'], 'new_lat' => null, 'new_lon' => null, 'distance_km' => null, 'source' => null, 'query' => null, 'osm_display' => null, 'status' => 'keep', 'note' => '', ]; // Target: hart gesetzte Koords if ($w['wp_type'] === 'target' && isset($TARGET_COORDS[$w['wp_key']])) { [$lat, $lon, $note] = $TARGET_COORDS[$w['wp_key']]; $p['new_lat'] = $lat; $p['new_lon'] = $lon; $p['source'] = 'manual'; $p['note'] = $note; $p['distance_km'] = round(haversine_km($p['old_lat'], $p['old_lon'], $lat, $lon), 3); if ($p['distance_km'] < 0.3) $p['status'] = 'keep'; elseif ($p['distance_km'] < 10.0) $p['status'] = 'safe_change'; else $p['status'] = 'suspicious'; $proposals[] = $p; continue; } // OSM-Query bestimmen $query = $OVERRIDE_QUERY[$w['wp_key']] ?? (clean_name($w['name']) . ', ' . ucfirst($w['region']) . ', Austria'); $p['query'] = $query; $osm = nominatim_lookup($query); usleep(1_100_000); if (!$osm) { $p['status'] = 'not_found'; $proposals[] = $p; continue; } $p['new_lat'] = $osm['lat']; $p['new_lon'] = $osm['lon']; $p['osm_display'] = $osm['display']; $p['source'] = 'osm'; $p['distance_km'] = round(haversine_km($p['old_lat'], $p['old_lon'], $osm['lat'], $osm['lon']), 3); if ($p['distance_km'] < 0.3) $p['status'] = 'keep'; elseif ($p['distance_km'] < 10.0) $p['status'] = 'safe_change'; else $p['status'] = 'suspicious'; $proposals[] = $p; } // --------------------------------------------------------------- // APPLY // --------------------------------------------------------------- $applied = []; $applyDone = false; if ($mode === 'apply' && $confirm === 'ja') { $stmt = $db->prepare("UPDATE geo_waypoints SET lat=?, lon=? WHERE wp_key=?"); foreach ($proposals as &$p) { $doApply = ($p['status'] === 'safe_change') || ($includeSusp && $p['status'] === 'suspicious'); if ($doApply && $p['new_lat'] !== null) { $stmt->execute([$p['new_lat'], $p['new_lon'], $p['wp_key']]); $p['applied'] = true; $applied[] = $p; } else { $p['applied'] = false; } } unset($p); $applyDone = true; } // --------------------------------------------------------------- // OUTPUT // --------------------------------------------------------------- $counts = array_count_values(array_column($proposals, 'status')); ?>
Pro Eintrag: OSM/Nominatim-Abfrage, Vergleich mit DB, Vorschlag. Targets mit manuellen Koords.
| Status | Typ | Key | Name | Alt | Neu | Δ km | Quelle | OSM-Treffer / Note |
|---|---|---|---|---|---|---|---|---|
| = htmlspecialchars($p['status']) ?>= !empty($p['applied']) ? ' ✓' : '' ?> | = htmlspecialchars($p['wp_type']) ?> | = htmlspecialchars($p['wp_key']) ?> |
= htmlspecialchars($p['name']) ?> | = number_format($p['old_lat'],5) ?>, = number_format($p['old_lon'],5) ?> |
= $p['new_lat'] !== null
? ''.number_format($p['new_lat'],5).', '.number_format($p['new_lon'],5).''
: '—' ?> |
= $p['distance_km'] !== null ? number_format($p['distance_km'],2) : '—' ?> | = htmlspecialchars($p['source'] ?? '—') ?> | = htmlspecialchars($p['osm_display'] ?? $p['note']) ?> |