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')); ?>
Quelle: OSM/Nominatim. Nur city und village werden geprüft. Grün < 500 m, Gelb < 2 km, Rot > 2 km.
| Status | Key | Name | Region | Typ | DB Lat, Lon | OSM Lat, Lon | Δ km | OSM-Treffer |
|---|---|---|---|---|---|---|---|---|
| = htmlspecialchars($r['status']) ?> | = htmlspecialchars($r['wp_key']) ?> |
= htmlspecialchars($r['name']) ?> | = htmlspecialchars($r['region']) ?> | = htmlspecialchars($r['wp_type']) ?> | = number_format($r['db_lat'], 5) ?>, = number_format($r['db_lon'], 5) ?> |
= $r['osm_lat'] !== null ? ''.number_format($r['osm_lat'],5).', '.number_format($r['osm_lon'],5).'' : '—' ?> |
= $r['distance_km'] !== null ? number_format($r['distance_km'], 2) : '—' ?> | = htmlspecialchars($r['osm_display'] ?? '') ?> |