Import jetzt ohne csv von Raumfahrzeugspuren möglich
This commit is contained in:
@@ -4,6 +4,9 @@ declare(strict_types=1);
|
||||
ini_set('display_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
const AU_IN_KM = 149597870.7;
|
||||
const IMPORT_END_TIME = '2046-04-12 00:00:00';
|
||||
|
||||
$config = require __DIR__ . '/../../config/database.php';
|
||||
$dsn = sprintf(
|
||||
'mysql:host=%s;dbname=%s;charset=%s',
|
||||
@@ -50,20 +53,174 @@ $missions = [
|
||||
[
|
||||
'key' => 'voyager1',
|
||||
'name' => 'Voyager 1',
|
||||
'csv' => __DIR__ . '/../../docs/voyager1_horizons_1977_2046_monthly_sun_centered.csv',
|
||||
'command' => "'-31'",
|
||||
'start_time' => '1977-09-05 14:00:00',
|
||||
'dense_stop_time' => '1989-12-05 14:00:00',
|
||||
],
|
||||
[
|
||||
'key' => 'voyager2',
|
||||
'name' => 'Voyager 2',
|
||||
'csv' => __DIR__ . '/../../docs/voyager2_horizons_1977_2046_monthly_sun_centered.csv',
|
||||
'command' => "'-32'",
|
||||
'start_time' => '1977-08-20 16:29:00',
|
||||
'dense_stop_time' => '1993-03-20 16:29:00',
|
||||
],
|
||||
[
|
||||
'key' => 'new_horizons',
|
||||
'name' => 'New Horizons',
|
||||
'csv' => __DIR__ . '/../../docs/new_horizons_2006_2046_monthly_sun_centered.csv',
|
||||
'command' => "'-98'",
|
||||
'start_time' => '2006-01-19 19:52:00',
|
||||
'dense_stop_time' => '2017-10-19 19:52:00',
|
||||
],
|
||||
];
|
||||
|
||||
function fetchHorizonsResult(array $extraParams): string
|
||||
{
|
||||
$params = array_merge([
|
||||
'format' => 'json',
|
||||
'OBJ_DATA' => "'NO'",
|
||||
'MAKE_EPHEM' => "'YES'",
|
||||
'EPHEM_TYPE' => "'VECTORS'",
|
||||
'CENTER' => "'500@10'",
|
||||
'REF_SYSTEM' => "'ICRF'",
|
||||
'REF_PLANE' => "'ECLIPTIC'",
|
||||
'VEC_TABLE' => "'2'",
|
||||
'VEC_CORR' => "'NONE'",
|
||||
'OUT_UNITS' => "'KM-S'",
|
||||
'TIME_TYPE' => "'TDB'",
|
||||
'CSV_FORMAT' => "'YES'",
|
||||
], $extraParams);
|
||||
|
||||
$query = http_build_query($params, '', '&');
|
||||
$query = str_replace(
|
||||
['%27', '%40', '%3A', '+'],
|
||||
["'", '@', ':', '%20'],
|
||||
$query
|
||||
);
|
||||
$url = 'https://ssd.jpl.nasa.gov/api/horizons.api?' . $query;
|
||||
$json = false;
|
||||
|
||||
if (function_exists('curl_init')) {
|
||||
$curlHandle = curl_init($url);
|
||||
if ($curlHandle !== false) {
|
||||
curl_setopt_array($curlHandle, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 30,
|
||||
CURLOPT_TIMEOUT => 180,
|
||||
CURLOPT_USERAGENT => 'skyview-spacecraft-import/1.0',
|
||||
]);
|
||||
$curlResponse = curl_exec($curlHandle);
|
||||
$curlError = curl_error($curlHandle);
|
||||
$httpCode = (int) curl_getinfo($curlHandle, CURLINFO_RESPONSE_CODE);
|
||||
curl_close($curlHandle);
|
||||
|
||||
if ($curlResponse !== false && $httpCode >= 200 && $httpCode < 300) {
|
||||
$json = $curlResponse;
|
||||
} elseif ($curlError !== '') {
|
||||
$json = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($json === false) {
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'method' => 'GET',
|
||||
'timeout' => 180,
|
||||
'header' => "User-Agent: skyview-spacecraft-import/1.0\r\n",
|
||||
],
|
||||
]);
|
||||
$json = @file_get_contents($url, false, $context);
|
||||
}
|
||||
|
||||
if ($json === false) {
|
||||
$escapedUrl = str_replace('"', '\"', $url);
|
||||
$command = 'curl.exe --silent --show-error --location --globoff "' . $escapedUrl . '"';
|
||||
$shellResponse = shell_exec($command);
|
||||
if (is_string($shellResponse) && $shellResponse !== '') {
|
||||
$json = $shellResponse;
|
||||
}
|
||||
}
|
||||
|
||||
if ($json === false) {
|
||||
throw new RuntimeException('Horizons API konnte nicht geladen werden: ' . $url);
|
||||
}
|
||||
|
||||
$payload = json_decode($json, true);
|
||||
if (!is_array($payload)) {
|
||||
throw new RuntimeException('Horizons API lieferte kein gueltiges JSON.');
|
||||
}
|
||||
|
||||
$result = (string) ($payload['result'] ?? '');
|
||||
if (isset($payload['error']) && $payload['error'] !== '') {
|
||||
throw new RuntimeException($result !== '' ? $result : (string) $payload['error']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function parseHorizonsVectorRows(string $result): array
|
||||
{
|
||||
$start = strpos($result, '$$SOE');
|
||||
$end = strpos($result, '$$EOE');
|
||||
if ($start === false || $end === false || $end <= $start) {
|
||||
throw new RuntimeException('Horizons-Antwort enthaelt keinen $$SOE/$$EOE-Block.');
|
||||
}
|
||||
|
||||
$block = trim(str_replace("\r", '', substr($result, $start + 5, $end - ($start + 5))));
|
||||
if ($block === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach (explode("\n", $block) as $rawLine) {
|
||||
$line = trim($rawLine);
|
||||
if ($line === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parts = array_map('trim', explode(',', $line));
|
||||
if (end($parts) === '') {
|
||||
array_pop($parts);
|
||||
}
|
||||
if (count($parts) !== 8) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$jdTdb = (float) $parts[0];
|
||||
$dateTdbRaw = (string) $parts[1];
|
||||
$dateTdbNormalized = preg_replace('/^A\.D\.\s+/i', '', $dateTdbRaw) ?? $dateTdbRaw;
|
||||
$date = DateTimeImmutable::createFromFormat('Y-M-d H:i:s.u', $dateTdbNormalized, new DateTimeZone('UTC'));
|
||||
if (!$date instanceof DateTimeImmutable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$xKm = (float) $parts[2];
|
||||
$yKm = (float) $parts[3];
|
||||
$zKm = (float) $parts[4];
|
||||
$vxKmS = (float) $parts[5];
|
||||
$vyKmS = (float) $parts[6];
|
||||
$vzKmS = (float) $parts[7];
|
||||
$distanceKm = sqrt(($xKm ** 2) + ($yKm ** 2) + ($zKm ** 2));
|
||||
|
||||
$rows[$date->format('Y-m-d H:i:s')] = [
|
||||
'date_tdb' => $date->format('Y-m-d H:i:s'),
|
||||
'jd_tdb' => $jdTdb,
|
||||
'x_km' => $xKm,
|
||||
'y_km' => $yKm,
|
||||
'z_km' => $zKm,
|
||||
'vx_km_s' => $vxKmS,
|
||||
'vy_km_s' => $vyKmS,
|
||||
'vz_km_s' => $vzKmS,
|
||||
'distance_km' => $distanceKm,
|
||||
'distance_au' => $distanceKm / AU_IN_KM,
|
||||
];
|
||||
}
|
||||
|
||||
ksort($rows);
|
||||
return array_values($rows);
|
||||
}
|
||||
|
||||
$insertStatement = $pdo->prepare("
|
||||
INSERT INTO `spacecraft_tracks` (
|
||||
`mission_key`,
|
||||
@@ -105,59 +262,65 @@ $insertStatement = $pdo->prepare("
|
||||
`distance_au` = VALUES(`distance_au`)
|
||||
");
|
||||
|
||||
$deleteStatement = $pdo->prepare('DELETE FROM `spacecraft_tracks` WHERE `mission_key` = :mission_key');
|
||||
|
||||
foreach ($missions as $mission) {
|
||||
if (!is_file($mission['csv'])) {
|
||||
throw new RuntimeException('CSV-Datei nicht gefunden: ' . $mission['csv']);
|
||||
$denseRows = parseHorizonsVectorRows(fetchHorizonsResult([
|
||||
'COMMAND' => $mission['command'],
|
||||
'START_TIME' => "'" . $mission['start_time'] . "'",
|
||||
'STOP_TIME' => "'" . $mission['dense_stop_time'] . "'",
|
||||
'STEP_SIZE' => "'1 d'",
|
||||
]));
|
||||
|
||||
$monthlyRows = parseHorizonsVectorRows(fetchHorizonsResult([
|
||||
'COMMAND' => $mission['command'],
|
||||
'START_TIME' => "'" . $mission['dense_stop_time'] . "'",
|
||||
'STOP_TIME' => "'" . IMPORT_END_TIME . "'",
|
||||
'STEP_SIZE' => "'1 mo'",
|
||||
]));
|
||||
|
||||
$finalRows = parseHorizonsVectorRows(fetchHorizonsResult([
|
||||
'COMMAND' => $mission['command'],
|
||||
'TLIST' => "'" . IMPORT_END_TIME . "'",
|
||||
]));
|
||||
|
||||
$rowsByDate = [];
|
||||
foreach (array_merge($denseRows, $monthlyRows, $finalRows) as $row) {
|
||||
$rowsByDate[$row['date_tdb']] = $row;
|
||||
}
|
||||
ksort($rowsByDate);
|
||||
$rows = array_values($rowsByDate);
|
||||
|
||||
$handle = fopen($mission['csv'], 'rb');
|
||||
if ($handle === false) {
|
||||
throw new RuntimeException('CSV-Datei konnte nicht geoeffnet werden: ' . $mission['csv']);
|
||||
}
|
||||
|
||||
$header = fgetcsv($handle, 0, ',', '"', '\\');
|
||||
if ($header === false) {
|
||||
fclose($handle);
|
||||
throw new RuntimeException('CSV-Kopf konnte nicht gelesen werden: ' . $mission['csv']);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
while (($values = fgetcsv($handle, 0, ',', '"', '\\')) !== false) {
|
||||
if (count($values) !== count($header)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row = array_combine($header, $values);
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dateTdbRaw = trim((string) ($row['date_tdb'] ?? ''));
|
||||
$dateTdbNormalized = preg_replace('/^A\.D\.\s+/i', '', $dateTdbRaw) ?? $dateTdbRaw;
|
||||
$date = DateTimeImmutable::createFromFormat('Y-M-d H:i:s.u', $dateTdbNormalized, new DateTimeZone('UTC'));
|
||||
if (!$date instanceof DateTimeImmutable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$insertStatement->execute([
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$deleteStatement->execute([
|
||||
':mission_key' => $mission['key'],
|
||||
':mission_name' => $mission['name'],
|
||||
':date_tdb' => $date->format('Y-m-d H:i:s'),
|
||||
':jd_tdb' => (float) $row['jd_tdb'],
|
||||
':x_km' => (float) $row['x_km'],
|
||||
':y_km' => (float) $row['y_km'],
|
||||
':z_km' => (float) $row['z_km'],
|
||||
':vx_km_s' => (float) $row['vx_km_s'],
|
||||
':vy_km_s' => (float) $row['vy_km_s'],
|
||||
':vz_km_s' => (float) $row['vz_km_s'],
|
||||
':distance_km' => (float) $row['distance_km'],
|
||||
':distance_au' => (float) $row['distance_au'],
|
||||
]);
|
||||
$count++;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$insertStatement->execute([
|
||||
':mission_key' => $mission['key'],
|
||||
':mission_name' => $mission['name'],
|
||||
':date_tdb' => $row['date_tdb'],
|
||||
':jd_tdb' => $row['jd_tdb'],
|
||||
':x_km' => $row['x_km'],
|
||||
':y_km' => $row['y_km'],
|
||||
':z_km' => $row['z_km'],
|
||||
':vx_km_s' => $row['vx_km_s'],
|
||||
':vy_km_s' => $row['vy_km_s'],
|
||||
':vz_km_s' => $row['vz_km_s'],
|
||||
':distance_km' => $row['distance_km'],
|
||||
':distance_au' => $row['distance_au'],
|
||||
]);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
} catch (Throwable $e) {
|
||||
$pdo->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
echo $mission['name'] . ': ' . $count . " Zeilen importiert.\n";
|
||||
echo $mission['name'] . ': ' . count($rows) . " Zeilen importiert.\n";
|
||||
}
|
||||
|
||||
echo "Import abgeschlossen.\n";
|
||||
|
||||
Reference in New Issue
Block a user