Files
geograsim/App/php/verify-waypoints.php
Adminator 1e51ef7def Nachtrag: alle bisher untracked Ordner + hängende Änderungen mit-committen
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/
- 12 code-workspace-Files
- STATUS-*.md
- viele M/D/R-Änderungen an bereits getrackten Files
- .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 02:27:02 +02:00

151 lines
6.1 KiB
PHP

<?php
/**
* Waypoint-Koordinaten-Audit gegen OpenStreetMap (Nominatim).
*
* Prüft alle city/village-Waypoints in geo_waypoints, vergleicht mit
* OSM-Koordinaten, listet Abweichungen. base/peak/target werden nur
* informativ angezeigt (keine Nominatim-Abfrage).
*
* Aufruf: http://localhost/geograsim/App/php/verify-waypoints.php
* http://localhost/geograsim/App/php/verify-waypoints.php?format=json
*/
require_once __DIR__ . '/config/app.php';
require_once __DIR__ . '/config/db.php';
set_time_limit(300);
$format = $_GET['format'] ?? 'html';
$db = getDB();
$rows = $db->query("SELECT wp_key, name, lat, lon, region, wp_type
FROM geo_waypoints
ORDER BY wp_type, region, name")->fetchAll();
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) {
// Entfernt "C8 ", "(ÖAMTC)" etc. für saubere Geocoding-Queries
$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-Audit/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']];
}
$results = [];
foreach ($rows as $w) {
$row = [
'wp_key' => $w['wp_key'],
'name' => $w['name'],
'region' => $w['region'],
'wp_type' => $w['wp_type'],
'db_lat' => (float)$w['lat'],
'db_lon' => (float)$w['lon'],
'osm_lat' => null, 'osm_lon' => null, 'distance_km' => null,
'osm_display' => null, 'status' => 'skipped',
];
if (in_array($w['wp_type'], ['city', 'village'])) {
$q = clean_name($w['name']) . ', ' . ucfirst($w['region']) . ', Austria';
$osm = nominatim_lookup($q);
usleep(1_100_000); // 1.1 s, Nominatim Usage Policy
if ($osm) {
$row['osm_lat'] = $osm['lat'];
$row['osm_lon'] = $osm['lon'];
$row['osm_display'] = $osm['display'];
$row['distance_km'] = round(haversine_km($row['db_lat'], $row['db_lon'], $osm['lat'], $osm['lon']), 3);
if ($row['distance_km'] < 0.5) $row['status'] = 'ok';
elseif ($row['distance_km'] < 2.0) $row['status'] = 'review';
else $row['status'] = 'wrong';
} else {
$row['status'] = 'not_found';
}
}
$results[] = $row;
}
if ($format === 'json') {
header('Content-Type: application/json; charset=utf-8');
echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
exit;
}
$counts = array_count_values(array_column($results, 'status'));
?><!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Waypoint-Audit</title>
<style>
body{font-family:system-ui,sans-serif;padding:1rem;background:#f5efe0;color:#1f2a24}
h1{color:#2d5434;margin-bottom:.5rem}
.sum{background:#fff;padding:.8rem 1rem;border-radius:8px;margin-bottom:1rem;display:flex;gap:1rem;flex-wrap:wrap}
.sum span{font-weight:700}
table{border-collapse:collapse;width:100%;background:#fff;font-size:.82rem}
th,td{padding:.35rem .55rem;border-bottom:1px solid #eee;text-align:left;vertical-align:top}
th{background:#2d5434;color:#f5efe0;position:sticky;top:0}
tr.ok td{background:#f2f8f2}
tr.review td{background:#fff6d9}
tr.wrong td{background:#fde0dc}
tr.not_found td{background:#f0e6f5}
tr.skipped td{color:#999}
code{font-family:ui-monospace,monospace;font-size:.78rem}
.dist{font-weight:700;text-align:right}
.btn{display:inline-block;padding:.4rem .8rem;background:#2d5434;color:#fff;border-radius:6px;text-decoration:none;margin-right:.5rem}
</style>
</head>
<body>
<h1>Waypoint-Koordinaten-Audit</h1>
<p style="color:#666;margin-bottom:1rem">Quelle: OSM/Nominatim. Nur <code>city</code> und <code>village</code> werden geprüft. Grün &lt; 500 m, Gelb &lt; 2 km, Rot &gt; 2 km.</p>
<div class="sum">
<div>Gesamt: <span><?= count($results) ?></span></div>
<div>OK: <span style="color:#3a6b3e"><?= $counts['ok'] ?? 0 ?></span></div>
<div>Review: <span style="color:#8a7020"><?= $counts['review'] ?? 0 ?></span></div>
<div>Falsch: <span style="color:#c85c4a"><?= $counts['wrong'] ?? 0 ?></span></div>
<div>Nicht gefunden: <span style="color:#7a4a8a"><?= $counts['not_found'] ?? 0 ?></span></div>
<div>Übersprungen: <span style="color:#888"><?= $counts['skipped'] ?? 0 ?></span></div>
<a class="btn" href="?format=json">JSON-Export</a>
</div>
<table>
<thead><tr>
<th>Status</th><th>Key</th><th>Name</th><th>Region</th><th>Typ</th>
<th>DB Lat, Lon</th><th>OSM Lat, Lon</th><th class="dist">Δ km</th><th>OSM-Treffer</th>
</tr></thead>
<tbody>
<?php foreach ($results as $r): ?>
<tr class="<?= htmlspecialchars($r['status']) ?>">
<td><?= htmlspecialchars($r['status']) ?></td>
<td><code><?= htmlspecialchars($r['wp_key']) ?></code></td>
<td><?= htmlspecialchars($r['name']) ?></td>
<td><?= htmlspecialchars($r['region']) ?></td>
<td><?= htmlspecialchars($r['wp_type']) ?></td>
<td><code><?= number_format($r['db_lat'], 5) ?>, <?= number_format($r['db_lon'], 5) ?></code></td>
<td><?= $r['osm_lat'] !== null ? '<code>'.number_format($r['osm_lat'],5).', '.number_format($r['osm_lon'],5).'</code>' : '—' ?></td>
<td class="dist"><?= $r['distance_km'] !== null ? number_format($r['distance_km'], 2) : '—' ?></td>
<td style="font-size:.72rem;color:#666"><?= htmlspecialchars($r['osm_display'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</body>
</html>