Erweitere astronomische Umrechnungen um Koordinaten und Winkel
This commit is contained in:
@@ -40,6 +40,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string) ($_POST['ajax'] ?? '') ===
|
|||||||
trim((string) ($_POST['sourceValue'] ?? '')),
|
trim((string) ($_POST['sourceValue'] ?? '')),
|
||||||
trim((string) ($_POST['longitude'] ?? '')),
|
trim((string) ($_POST['longitude'] ?? '')),
|
||||||
]);
|
]);
|
||||||
|
} elseif ($kind === 'coordinates') {
|
||||||
|
$pythonResult = runPythonApi('astronomical_conversions', [
|
||||||
|
'coordinates',
|
||||||
|
trim((string) ($_POST['sourceSystem'] ?? '')),
|
||||||
|
trim((string) ($_POST['value1'] ?? '')),
|
||||||
|
trim((string) ($_POST['value2'] ?? '')),
|
||||||
|
]);
|
||||||
|
} elseif ($kind === 'angle') {
|
||||||
|
$pythonResult = runPythonApi('astronomical_conversions', [
|
||||||
|
'angle',
|
||||||
|
trim((string) ($_POST['sourceUnit'] ?? '')),
|
||||||
|
trim((string) ($_POST['sourceValue'] ?? '')),
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
@@ -213,6 +226,8 @@ $timeLocationLongitude = isset($currentLocation['longitude']) && is_numeric($cur
|
|||||||
<button type="button" class="astro-conv-tab is-active" data-tab-target="distanceTab" role="tab" aria-selected="true">Entfernung</button>
|
<button type="button" class="astro-conv-tab is-active" data-tab-target="distanceTab" role="tab" aria-selected="true">Entfernung</button>
|
||||||
<button type="button" class="astro-conv-tab" data-tab-target="redshiftTab" role="tab" aria-selected="false">Rotverschiebung</button>
|
<button type="button" class="astro-conv-tab" data-tab-target="redshiftTab" role="tab" aria-selected="false">Rotverschiebung</button>
|
||||||
<button type="button" class="astro-conv-tab" data-tab-target="timeTab" role="tab" aria-selected="false">Zeit</button>
|
<button type="button" class="astro-conv-tab" data-tab-target="timeTab" role="tab" aria-selected="false">Zeit</button>
|
||||||
|
<button type="button" class="astro-conv-tab" data-tab-target="coordinatesTab" role="tab" aria-selected="false">Koordinaten</button>
|
||||||
|
<button type="button" class="astro-conv-tab" data-tab-target="angleTab" role="tab" aria-selected="false">Winkel</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="card astro-conv-panel astro-conv-tab-panel is-active" id="distanceTab" role="tabpanel">
|
<section class="card astro-conv-panel astro-conv-tab-panel is-active" id="distanceTab" role="tabpanel">
|
||||||
@@ -416,6 +431,72 @@ $timeLocationLongitude = isset($currentLocation['longitude']) && is_numeric($cur
|
|||||||
<button type="button" class="btn btn-primary" id="timeJ2000">J2000</button>
|
<button type="button" class="btn btn-primary" id="timeJ2000">J2000</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="card astro-conv-panel astro-conv-tab-panel" id="coordinatesTab" role="tabpanel">
|
||||||
|
<h2>Koordinaten-Umrechner</h2>
|
||||||
|
<p>Umrechnung zwischen äquatorialen und galaktischen Koordinaten auf Basis des J2000-Systems. Eingabe und Ausgabe erfolgen in Dezimalgrad.</p>
|
||||||
|
|
||||||
|
<div class="astro-conv-grid" id="coordinatesConverter">
|
||||||
|
<div class="astro-conv-field">
|
||||||
|
<label for="coordRa"><span class="astro-conv-label-help" title="Rektaszension α im äquatorialen System, in Dezimalgrad von 0° bis 360°.">Rektaszension α (°)</span></label>
|
||||||
|
<input class="astro-conv-input" id="coordRa" type="text" inputmode="decimal" value="279,234735">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-conv-field">
|
||||||
|
<label for="coordDec"><span class="astro-conv-label-help" title="Deklination δ im äquatorialen System, in Dezimalgrad von -90° bis +90°.">Deklination δ (°)</span></label>
|
||||||
|
<input class="astro-conv-input" id="coordDec" type="text" inputmode="decimal" value="38,783689">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-conv-field">
|
||||||
|
<label for="coordGalLon"><span class="astro-conv-label-help" title="Galaktische Länge l in Dezimalgrad von 0° bis 360°.">Galaktische Länge l (°)</span></label>
|
||||||
|
<input class="astro-conv-input" id="coordGalLon" type="text" inputmode="decimal">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-conv-field">
|
||||||
|
<label for="coordGalLat"><span class="astro-conv-label-help" title="Galaktische Breite b in Dezimalgrad von -90° bis +90°.">Galaktische Breite b (°)</span></label>
|
||||||
|
<input class="astro-conv-input" id="coordGalLat" type="text" inputmode="decimal">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-conv-actions">
|
||||||
|
<button type="button" class="btn btn-secondary" id="coordinatesClear">Leeren</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="coordinatesVega">Vega</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="coordinatesGalacticCenter">Galaktisches Zentrum</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card astro-conv-panel astro-conv-tab-panel" id="angleTab" role="tabpanel">
|
||||||
|
<h2>Winkel-Umrechner</h2>
|
||||||
|
<p>Umrechnung zwischen Grad, Bogenminute, Bogensekunde und Radiant.</p>
|
||||||
|
|
||||||
|
<div class="astro-conv-grid" id="angleConverter">
|
||||||
|
<div class="astro-conv-field">
|
||||||
|
<label for="angleDeg">Grad (°)</label>
|
||||||
|
<input class="astro-conv-input" id="angleDeg" type="text" inputmode="decimal" value="1">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-conv-field">
|
||||||
|
<label for="angleArcmin">Bogenminute (′)</label>
|
||||||
|
<input class="astro-conv-input" id="angleArcmin" type="text" inputmode="decimal">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-conv-field">
|
||||||
|
<label for="angleArcsec">Bogensekunde (″)</label>
|
||||||
|
<input class="astro-conv-input" id="angleArcsec" type="text" inputmode="decimal">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-conv-field">
|
||||||
|
<label for="angleRad">Radiant (rad)</label>
|
||||||
|
<input class="astro-conv-input" id="angleRad" type="text" inputmode="decimal">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-conv-actions">
|
||||||
|
<button type="button" class="btn btn-secondary" id="angleClear">Leeren</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="angleDegree">1 Grad</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="angleArcsecond">1 Bogensekunde</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="js/number_to_german_words.js"></script>
|
<script src="js/number_to_german_words.js"></script>
|
||||||
@@ -477,11 +558,25 @@ $timeLocationLongitude = isset($currentLocation['longitude']) && is_numeric($cur
|
|||||||
trueSolar: document.getElementById('timeTrueSolar'),
|
trueSolar: document.getElementById('timeTrueSolar'),
|
||||||
sidereal: document.getElementById('timeSidereal')
|
sidereal: document.getElementById('timeSidereal')
|
||||||
};
|
};
|
||||||
|
const coordinateFields = {
|
||||||
|
ra: document.getElementById('coordRa'),
|
||||||
|
dec: document.getElementById('coordDec'),
|
||||||
|
galLon: document.getElementById('coordGalLon'),
|
||||||
|
galLat: document.getElementById('coordGalLat')
|
||||||
|
};
|
||||||
|
const angleFields = {
|
||||||
|
deg: document.getElementById('angleDeg'),
|
||||||
|
arcmin: document.getElementById('angleArcmin'),
|
||||||
|
arcsec: document.getElementById('angleArcsec'),
|
||||||
|
rad: document.getElementById('angleRad')
|
||||||
|
};
|
||||||
let isSyncingTimePicker = false;
|
let isSyncingTimePicker = false;
|
||||||
let timeUtcPicker = null;
|
let timeUtcPicker = null;
|
||||||
let distanceRequestId = 0;
|
let distanceRequestId = 0;
|
||||||
let redshiftRequestId = 0;
|
let redshiftRequestId = 0;
|
||||||
let timeRequestId = 0;
|
let timeRequestId = 0;
|
||||||
|
let coordinateRequestId = 0;
|
||||||
|
let angleRequestId = 0;
|
||||||
|
|
||||||
const DECIMAL_DIVISION_PRECISION = 32;
|
const DECIMAL_DIVISION_PRECISION = 32;
|
||||||
const pow10Cache = [1n];
|
const pow10Cache = [1n];
|
||||||
@@ -893,6 +988,18 @@ $timeLocationLongitude = isset($currentLocation['longitude']) && is_numeric($cur
|
|||||||
wireTooltipHover(field);
|
wireTooltipHover(field);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const coordinateFieldEntries = Object.entries(coordinateFields);
|
||||||
|
coordinateFieldEntries.forEach(function ([unit, field]) {
|
||||||
|
attachTooltip(field, 'coordinates-' + unit);
|
||||||
|
wireTooltipHover(field);
|
||||||
|
});
|
||||||
|
|
||||||
|
const angleFieldEntries = Object.entries(angleFields);
|
||||||
|
angleFieldEntries.forEach(function ([unit, field]) {
|
||||||
|
attachTooltip(field, 'angle-' + unit);
|
||||||
|
wireTooltipHover(field);
|
||||||
|
});
|
||||||
|
|
||||||
const updateDistanceFrom = async function (sourceUnit) {
|
const updateDistanceFrom = async function (sourceUnit) {
|
||||||
const rawValue = distanceFields[sourceUnit].value.trim();
|
const rawValue = distanceFields[sourceUnit].value.trim();
|
||||||
if (rawValue === '') {
|
if (rawValue === '') {
|
||||||
@@ -1339,6 +1446,133 @@ $timeLocationLongitude = isset($currentLocation['longitude']) && is_numeric($cur
|
|||||||
updateTimeFrom('utc');
|
updateTimeFrom('utc');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const clearCoordinateGroup = function (keys) {
|
||||||
|
keys.forEach(function (key) {
|
||||||
|
if (coordinateFields[key]) {
|
||||||
|
coordinateFields[key].value = '';
|
||||||
|
updateTooltip(coordinateFields[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateCoordinatesFrom = async function (sourceSystem) {
|
||||||
|
const isEquatorial = sourceSystem === 'equatorial';
|
||||||
|
const firstKey = isEquatorial ? 'ra' : 'galLon';
|
||||||
|
const secondKey = isEquatorial ? 'dec' : 'galLat';
|
||||||
|
const targetKeys = isEquatorial ? ['galLon', 'galLat'] : ['ra', 'dec'];
|
||||||
|
const firstValue = coordinateFields[firstKey].value.trim();
|
||||||
|
const secondValue = coordinateFields[secondKey].value.trim();
|
||||||
|
|
||||||
|
if (firstValue === '' || secondValue === '') {
|
||||||
|
clearCoordinateGroup(targetKeys);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestId = ++coordinateRequestId;
|
||||||
|
try {
|
||||||
|
const result = await requestConversion({
|
||||||
|
kind: 'coordinates',
|
||||||
|
sourceSystem,
|
||||||
|
value1: firstValue,
|
||||||
|
value2: secondValue
|
||||||
|
});
|
||||||
|
|
||||||
|
if (requestId !== coordinateRequestId || !result.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFieldValues(coordinateFields, result.fields || {});
|
||||||
|
} catch (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
['ra', 'dec'].forEach(function (key) {
|
||||||
|
coordinateFields[key].addEventListener('input', function () {
|
||||||
|
updateCoordinatesFrom('equatorial');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
['galLon', 'galLat'].forEach(function (key) {
|
||||||
|
coordinateFields[key].addEventListener('input', function () {
|
||||||
|
updateCoordinatesFrom('galactic');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('coordinatesClear').addEventListener('click', function () {
|
||||||
|
Object.values(coordinateFields).forEach(function (field) {
|
||||||
|
field.value = '';
|
||||||
|
updateTooltip(field);
|
||||||
|
});
|
||||||
|
coordinateFields.ra.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('coordinatesVega').addEventListener('click', function () {
|
||||||
|
coordinateFields.ra.value = '279,234735';
|
||||||
|
coordinateFields.dec.value = '38,783689';
|
||||||
|
updateCoordinatesFrom('equatorial');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('coordinatesGalacticCenter').addEventListener('click', function () {
|
||||||
|
coordinateFields.galLon.value = '0';
|
||||||
|
coordinateFields.galLat.value = '0';
|
||||||
|
updateCoordinatesFrom('galactic');
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAngleFrom = async function (sourceUnit) {
|
||||||
|
const rawValue = angleFields[sourceUnit].value.trim();
|
||||||
|
if (rawValue === '') {
|
||||||
|
Object.entries(angleFields).forEach(function ([key, field]) {
|
||||||
|
if (key !== sourceUnit) {
|
||||||
|
field.value = '';
|
||||||
|
updateTooltip(field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestId = ++angleRequestId;
|
||||||
|
try {
|
||||||
|
const result = await requestConversion({
|
||||||
|
kind: 'angle',
|
||||||
|
sourceUnit,
|
||||||
|
sourceValue: rawValue
|
||||||
|
});
|
||||||
|
|
||||||
|
if (requestId !== angleRequestId || !result.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFieldValues(angleFields, result.fields || {});
|
||||||
|
} catch (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
angleFieldEntries.forEach(function ([unit, field]) {
|
||||||
|
field.addEventListener('input', function () {
|
||||||
|
updateAngleFrom(unit);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('angleClear').addEventListener('click', function () {
|
||||||
|
Object.values(angleFields).forEach(function (field) {
|
||||||
|
field.value = '';
|
||||||
|
updateTooltip(field);
|
||||||
|
});
|
||||||
|
angleFields.deg.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('angleDegree').addEventListener('click', function () {
|
||||||
|
angleFields.deg.value = '1';
|
||||||
|
updateAngleFrom('deg');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('angleArcsecond').addEventListener('click', function () {
|
||||||
|
angleFields.arcsec.value = '1';
|
||||||
|
updateAngleFrom('arcsec');
|
||||||
|
});
|
||||||
|
|
||||||
const tabs = Array.from(document.querySelectorAll('.astro-conv-tab'));
|
const tabs = Array.from(document.querySelectorAll('.astro-conv-tab'));
|
||||||
const tabPanels = Array.from(document.querySelectorAll('.astro-conv-tab-panel'));
|
const tabPanels = Array.from(document.querySelectorAll('.astro-conv-tab-panel'));
|
||||||
|
|
||||||
@@ -1363,6 +1597,8 @@ $timeLocationLongitude = isset($currentLocation['longitude']) && is_numeric($cur
|
|||||||
updateDistanceFrom('km');
|
updateDistanceFrom('km');
|
||||||
updateRedshift();
|
updateRedshift();
|
||||||
updateTimeFrom('utc');
|
updateTimeFrom('utc');
|
||||||
|
updateCoordinatesFrom('equatorial');
|
||||||
|
updateAngleFrom('deg');
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -241,6 +241,23 @@ def action_astronomical_conversions(args: list[str]) -> dict:
|
|||||||
payload["sourceUnit"] = args[1]
|
payload["sourceUnit"] = args[1]
|
||||||
payload["sourceValue"] = args[2]
|
payload["sourceValue"] = args[2]
|
||||||
payload["longitude"] = args[3]
|
payload["longitude"] = args[3]
|
||||||
|
elif kind == "coordinates":
|
||||||
|
if len(args) != 4:
|
||||||
|
fail(
|
||||||
|
"Aktion astronomical_conversions fuer coordinates erwartet 4 Argumente: kind sourceSystem value1 value2",
|
||||||
|
extra={"argv": args},
|
||||||
|
)
|
||||||
|
payload["sourceSystem"] = args[1]
|
||||||
|
payload["value1"] = args[2]
|
||||||
|
payload["value2"] = args[3]
|
||||||
|
elif kind == "angle":
|
||||||
|
if len(args) != 3:
|
||||||
|
fail(
|
||||||
|
"Aktion astronomical_conversions fuer angle erwartet 3 Argumente: kind sourceUnit sourceValue",
|
||||||
|
extra={"argv": args},
|
||||||
|
)
|
||||||
|
payload["sourceUnit"] = args[1]
|
||||||
|
payload["sourceValue"] = args[2]
|
||||||
else:
|
else:
|
||||||
fail(
|
fail(
|
||||||
"Unbekannter Umrechnungsbereich fuer astronomical_conversions.",
|
"Unbekannter Umrechnungsbereich fuer astronomical_conversions.",
|
||||||
|
|||||||
@@ -55,6 +55,14 @@ REDSHIFT_ORDER = [
|
|||||||
"angularDiameterMpc",
|
"angularDiameterMpc",
|
||||||
]
|
]
|
||||||
TIME_ORDER = ["utc", "ut1", "tai", "jd", "mjd", "unix", "trueSolar", "sidereal"]
|
TIME_ORDER = ["utc", "ut1", "tai", "jd", "mjd", "unix", "trueSolar", "sidereal"]
|
||||||
|
COORDINATE_ORDER = ["ra", "dec", "galLon", "galLat"]
|
||||||
|
ANGLE_ORDER = ["deg", "arcmin", "arcsec", "rad"]
|
||||||
|
ANGLE_TO_DEGREES = {
|
||||||
|
"deg": Decimal("1"),
|
||||||
|
"arcmin": Decimal("0.01666666666666666666666666667"),
|
||||||
|
"arcsec": Decimal("0.0002777777777777777777777777778"),
|
||||||
|
"rad": DECIMAL_180 / DECIMAL_PI,
|
||||||
|
}
|
||||||
LEAP_SECONDS = [
|
LEAP_SECONDS = [
|
||||||
("1972-01-01T00:00:00Z", 10),
|
("1972-01-01T00:00:00Z", 10),
|
||||||
("1972-07-01T00:00:00Z", 11),
|
("1972-07-01T00:00:00Z", 11),
|
||||||
@@ -86,13 +94,26 @@ LEAP_SECONDS = [
|
|||||||
("2017-01-01T00:00:00Z", 37),
|
("2017-01-01T00:00:00Z", 37),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
EQ_TO_GAL_MATRIX = (
|
||||||
|
(-0.0548755604162154, -0.8734370902348850, -0.4838350155487132),
|
||||||
|
(0.4941094278755837, -0.4448296299600112, 0.7469822444972189),
|
||||||
|
(-0.8676661490190047, -0.1980763734312015, 0.4559837761750669),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_decimal_input(value: str) -> Decimal | None:
|
def parse_decimal_input(value: str) -> Decimal | None:
|
||||||
text = str(value or "").strip()
|
text = str(value or "").strip()
|
||||||
if text == "":
|
if text == "":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
normalized = text.replace(" ", "").replace(".", "").replace(",", ".")
|
normalized = text.replace(" ", "")
|
||||||
|
if "," in normalized and "." in normalized:
|
||||||
|
if normalized.rfind(",") > normalized.rfind("."):
|
||||||
|
normalized = normalized.replace(".", "").replace(",", ".")
|
||||||
|
else:
|
||||||
|
normalized = normalized.replace(",", "")
|
||||||
|
elif "," in normalized:
|
||||||
|
normalized = normalized.replace(".", "").replace(",", ".")
|
||||||
if normalized.count(".") > 1:
|
if normalized.count(".") > 1:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -147,6 +168,26 @@ def format_clock(hours_value: float) -> str:
|
|||||||
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_angle_degrees(value: float) -> float:
|
||||||
|
normalized = math.fmod(value, 360.0)
|
||||||
|
if normalized < 0.0:
|
||||||
|
normalized += 360.0
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
|
||||||
|
def transpose_matrix(matrix: tuple[tuple[float, float, float], ...]) -> tuple[tuple[float, float, float], ...]:
|
||||||
|
return tuple(tuple(matrix[row][column] for row in range(3)) for column in range(3))
|
||||||
|
|
||||||
|
|
||||||
|
def multiply_matrix_vector(
|
||||||
|
matrix: tuple[tuple[float, float, float], ...], vector: tuple[float, float, float]
|
||||||
|
) -> tuple[float, float, float]:
|
||||||
|
return tuple(sum(matrix_row[index] * vector[index] for index in range(3)) for matrix_row in matrix)
|
||||||
|
|
||||||
|
|
||||||
|
GAL_TO_EQ_MATRIX = transpose_matrix(EQ_TO_GAL_MATRIX)
|
||||||
|
|
||||||
|
|
||||||
def leap_seconds_for_utc(dt_utc: datetime) -> int:
|
def leap_seconds_for_utc(dt_utc: datetime) -> int:
|
||||||
count = 0
|
count = 0
|
||||||
for effective_iso, total_offset in LEAP_SECONDS:
|
for effective_iso, total_offset in LEAP_SECONDS:
|
||||||
@@ -244,6 +285,27 @@ def adaptive_simpson(fn, start: float, end: float, epsilon: float = 1e-10, max_d
|
|||||||
return recurse(start, end, epsilon, whole, left_value, middle_value, right_value, max_depth)
|
return recurse(start, end, epsilon, whole, left_value, middle_value, right_value, max_depth)
|
||||||
|
|
||||||
|
|
||||||
|
def spherical_to_cartesian(longitude_deg: float, latitude_deg: float) -> tuple[float, float, float]:
|
||||||
|
longitude_rad = math.radians(longitude_deg)
|
||||||
|
latitude_rad = math.radians(latitude_deg)
|
||||||
|
cos_latitude = math.cos(latitude_rad)
|
||||||
|
return (
|
||||||
|
cos_latitude * math.cos(longitude_rad),
|
||||||
|
cos_latitude * math.sin(longitude_rad),
|
||||||
|
math.sin(latitude_rad),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def cartesian_to_spherical(x: float, y: float, z: float) -> tuple[float, float]:
|
||||||
|
radius = math.sqrt((x * x) + (y * y) + (z * z))
|
||||||
|
if radius == 0.0:
|
||||||
|
raise ValueError("Koordinatenvektor darf nicht null sein.")
|
||||||
|
|
||||||
|
longitude_deg = normalize_angle_degrees(math.degrees(math.atan2(y, x)))
|
||||||
|
latitude_deg = math.degrees(math.asin(max(-1.0, min(1.0, z / radius))))
|
||||||
|
return longitude_deg, latitude_deg
|
||||||
|
|
||||||
|
|
||||||
def convert_distance(payload: dict) -> dict:
|
def convert_distance(payload: dict) -> dict:
|
||||||
source_unit = str(payload.get("sourceUnit") or "").strip().lower()
|
source_unit = str(payload.get("sourceUnit") or "").strip().lower()
|
||||||
source_value = parse_decimal_input(payload.get("sourceValue", ""))
|
source_value = parse_decimal_input(payload.get("sourceValue", ""))
|
||||||
@@ -352,23 +414,26 @@ def convert_time(payload: dict) -> dict:
|
|||||||
"sidereal": "",
|
"sidereal": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
leap_seconds = leap_seconds_for_utc(dt_utc)
|
||||||
|
ut1_fallback = format_german_datetime(dt_utc) + " (nahezu UTC)"
|
||||||
|
tai_fallback = format_german_datetime(dt_utc + timedelta(seconds=leap_seconds))
|
||||||
|
|
||||||
if ASTROPY_AVAILABLE:
|
if ASTROPY_AVAILABLE:
|
||||||
astropy_time = AstropyTime(dt_utc, scale="utc")
|
astropy_time = AstropyTime(dt_utc, scale="utc")
|
||||||
try:
|
try:
|
||||||
ut1_datetime = astropy_time.ut1.to_datetime(timezone=timezone.utc)
|
ut1_datetime = astropy_time.ut1.to_datetime(timezone=timezone.utc)
|
||||||
fields["ut1"] = format_german_datetime(ut1_datetime)
|
fields["ut1"] = format_german_datetime(ut1_datetime)
|
||||||
except Exception:
|
except Exception:
|
||||||
fields["ut1"] = "Nicht verfuegbar"
|
fields["ut1"] = ut1_fallback
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tai_datetime = astropy_time.tai.to_datetime(timezone=timezone.utc)
|
tai_datetime = astropy_time.tai.to_datetime(timezone=timezone.utc)
|
||||||
fields["tai"] = format_german_datetime(tai_datetime)
|
fields["tai"] = format_german_datetime(tai_datetime)
|
||||||
except Exception:
|
except Exception:
|
||||||
fields["tai"] = "Nicht verfuegbar"
|
fields["tai"] = tai_fallback
|
||||||
else:
|
else:
|
||||||
leap_seconds = leap_seconds_for_utc(dt_utc)
|
fields["ut1"] = ut1_fallback
|
||||||
fields["ut1"] = format_german_datetime(dt_utc) + " (nahezu UTC)"
|
fields["tai"] = tai_fallback
|
||||||
fields["tai"] = format_german_datetime(dt_utc + timedelta(seconds=leap_seconds))
|
|
||||||
|
|
||||||
if longitude_value is not None and math.isfinite(longitude_value):
|
if longitude_value is not None and math.isfinite(longitude_value):
|
||||||
observer = astronomy.Observer(0.0, longitude_value, 0.0)
|
observer = astronomy.Observer(0.0, longitude_value, 0.0)
|
||||||
@@ -393,6 +458,57 @@ def convert_time(payload: dict) -> dict:
|
|||||||
return {"valid": True, "fields": fields}
|
return {"valid": True, "fields": fields}
|
||||||
|
|
||||||
|
|
||||||
|
def convert_coordinates(payload: dict) -> dict:
|
||||||
|
source_system = str(payload.get("sourceSystem") or "").strip().lower()
|
||||||
|
first_value = parse_decimal_input(payload.get("value1", ""))
|
||||||
|
second_value = parse_decimal_input(payload.get("value2", ""))
|
||||||
|
|
||||||
|
if first_value is None or second_value is None:
|
||||||
|
return {"valid": False, "fields": {key: "" for key in COORDINATE_ORDER}}
|
||||||
|
|
||||||
|
longitude = float(first_value)
|
||||||
|
latitude = float(second_value)
|
||||||
|
if not math.isfinite(longitude) or not math.isfinite(latitude) or latitude < -90.0 or latitude > 90.0:
|
||||||
|
return {"valid": False, "fields": {key: "" for key in COORDINATE_ORDER}}
|
||||||
|
|
||||||
|
if source_system == "equatorial":
|
||||||
|
equatorial_vector = spherical_to_cartesian(normalize_angle_degrees(longitude), latitude)
|
||||||
|
galactic_vector = multiply_matrix_vector(EQ_TO_GAL_MATRIX, equatorial_vector)
|
||||||
|
ra_deg, dec_deg = cartesian_to_spherical(*equatorial_vector)
|
||||||
|
gal_lon_deg, gal_lat_deg = cartesian_to_spherical(*galactic_vector)
|
||||||
|
elif source_system == "galactic":
|
||||||
|
galactic_vector = spherical_to_cartesian(normalize_angle_degrees(longitude), latitude)
|
||||||
|
equatorial_vector = multiply_matrix_vector(GAL_TO_EQ_MATRIX, galactic_vector)
|
||||||
|
gal_lon_deg, gal_lat_deg = cartesian_to_spherical(*galactic_vector)
|
||||||
|
ra_deg, dec_deg = cartesian_to_spherical(*equatorial_vector)
|
||||||
|
else:
|
||||||
|
raise ValueError("Unbekanntes Koordinatensystem.")
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
"ra": format_decimal_german(ra_deg, max_fraction_digits=6),
|
||||||
|
"dec": format_decimal_german(dec_deg, max_fraction_digits=6),
|
||||||
|
"galLon": format_decimal_german(gal_lon_deg, max_fraction_digits=6),
|
||||||
|
"galLat": format_decimal_german(gal_lat_deg, max_fraction_digits=6),
|
||||||
|
}
|
||||||
|
return {"valid": True, "fields": fields}
|
||||||
|
|
||||||
|
|
||||||
|
def convert_angle(payload: dict) -> dict:
|
||||||
|
source_unit = str(payload.get("sourceUnit") or "").strip().lower()
|
||||||
|
source_value = parse_decimal_input(payload.get("sourceValue", ""))
|
||||||
|
|
||||||
|
if source_unit not in ANGLE_TO_DEGREES or source_value is None:
|
||||||
|
return {"valid": False, "fields": {key: "" for key in ANGLE_ORDER}}
|
||||||
|
|
||||||
|
value_in_degrees = source_value * ANGLE_TO_DEGREES[source_unit]
|
||||||
|
fields: dict[str, str] = {}
|
||||||
|
for unit in ANGLE_ORDER:
|
||||||
|
converted = value_in_degrees / ANGLE_TO_DEGREES[unit]
|
||||||
|
fields[unit] = format_decimal_german(converted, max_fraction_digits=12)
|
||||||
|
|
||||||
|
return {"valid": True, "fields": fields}
|
||||||
|
|
||||||
|
|
||||||
def handle_request(payload: dict) -> dict:
|
def handle_request(payload: dict) -> dict:
|
||||||
kind = str(payload.get("kind") or "").strip().lower()
|
kind = str(payload.get("kind") or "").strip().lower()
|
||||||
if kind == "distance":
|
if kind == "distance":
|
||||||
@@ -401,6 +517,10 @@ def handle_request(payload: dict) -> dict:
|
|||||||
result = convert_redshift(payload)
|
result = convert_redshift(payload)
|
||||||
elif kind == "time":
|
elif kind == "time":
|
||||||
result = convert_time(payload)
|
result = convert_time(payload)
|
||||||
|
elif kind == "coordinates":
|
||||||
|
result = convert_coordinates(payload)
|
||||||
|
elif kind == "angle":
|
||||||
|
result = convert_angle(payload)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unbekannter Umrechnungsbereich.")
|
raise ValueError("Unbekannter Umrechnungsbereich.")
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal
|
||||||
|
|
||||||
set "REPO_DIR=%~dp0.."
|
set "REPO_DIR=%~dp0"
|
||||||
set "SERVER_URL=http://127.0.0.1:8000/"
|
set "SERVER_URL=http://127.0.0.1:8000/"
|
||||||
|
|
||||||
cd /d "%REPO_DIR%"
|
cd /d "%REPO_DIR%"
|
||||||
Reference in New Issue
Block a user