feature(helioviewer): move helioviewer skript to its dedicated git repository
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Holt ein Sonnen-Vollscheibenbild von Helioviewer zu einem Zeitpunkt.
|
||||
|
||||
Beispiel:
|
||||
./helioviewer_grab.py 2026-08-12T18:18:00Z hmi_continuum
|
||||
./helioviewer_grab.py --list
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
API = "https://api.helioviewer.org/v2"
|
||||
|
||||
|
||||
def call(endpoint, **params):
|
||||
"""GET auf die Helioviewer-API. Gibt (content_type, bytes) zurueck."""
|
||||
url = f"{API}/{endpoint}/?" + urllib.parse.urlencode(params)
|
||||
with urllib.request.urlopen(url, timeout=120) as r:
|
||||
return r.headers.get_content_type(), r.read()
|
||||
|
||||
|
||||
def sources():
|
||||
"""Flache Liste aller Datenquellen: [(sourceId, layer_path, nickname), ...]
|
||||
|
||||
Der Pfad durch den verschachtelten JSON-Baum IST die Layer-Kette,
|
||||
die takeScreenshot erwartet (z.B. SDO/HMI/HMI/continuum).
|
||||
"""
|
||||
_, raw = call("getDataSources")
|
||||
out = []
|
||||
|
||||
def walk(node, path):
|
||||
if not isinstance(node, dict):
|
||||
return
|
||||
if "sourceId" in node:
|
||||
out.append((node["sourceId"], path, node.get("nickname", "")))
|
||||
return
|
||||
for key, val in node.items():
|
||||
walk(val, path + [key])
|
||||
|
||||
walk(json.loads(raw), [])
|
||||
return sorted(out)
|
||||
|
||||
|
||||
def pick(needle):
|
||||
"""Sucht eine Datenquelle anhand eines Substrings im Nickname/Pfad."""
|
||||
hits = [
|
||||
s
|
||||
for s in sources()
|
||||
if needle.lower().replace("_", " ")
|
||||
in (" ".join(s[1]) + " " + s[2]).lower().replace("_", " ")
|
||||
]
|
||||
if not hits:
|
||||
sys.exit(f"keine Datenquelle passt auf {needle!r} (siehe --list)")
|
||||
return hits[0]
|
||||
|
||||
|
||||
def main():
|
||||
if "--list" in sys.argv:
|
||||
for sid, path, nick in sources():
|
||||
print(f"{sid:4d} {'/'.join(path):40s} {nick}")
|
||||
return
|
||||
|
||||
date = sys.argv[1] if len(sys.argv) > 1 else "2026-08-12T18:18:00Z"
|
||||
what = sys.argv[2] if len(sys.argv) > 2 else "continuum"
|
||||
|
||||
sid, path, nick = pick(what)
|
||||
print(f"Quelle: [{sid}] {'/'.join(path)} ({nick})")
|
||||
|
||||
# Welches Bild liegt dem Wunschzeitpunkt am naechsten? Wichtig:
|
||||
# Helioviewer liefert IMMER etwas, auch wenn es Stunden daneben liegt.
|
||||
_, raw = call("getClosestImage", date=date, sourceId=sid)
|
||||
meta = json.loads(raw)
|
||||
print(f"angefragt: {date}\ngeliefert: {meta['date']}")
|
||||
|
||||
layers = "[" + ",".join(path) + ",1,100]"
|
||||
ctype, png = call(
|
||||
"takeScreenshot",
|
||||
date=meta["date"],
|
||||
imageScale=0.6, # Bogensekunden/Pixel -> ~3400 px Vollscheibe
|
||||
layers=layers,
|
||||
x1=-1100,
|
||||
y1=-1100,
|
||||
x2=1100,
|
||||
y2=1100, # Bogensekunden ab Scheibenmitte
|
||||
display="true",
|
||||
watermark="false",
|
||||
)
|
||||
if not ctype.startswith("image"):
|
||||
sys.exit(f"unerwartete Antwort ({ctype}): {png[:200]!r}")
|
||||
|
||||
name = (
|
||||
f"{meta['date'].replace(':', '').replace('-', '')}_{nick.replace(' ', '')}.png"
|
||||
)
|
||||
with open(name, "wb") as f:
|
||||
f.write(png)
|
||||
print(f"-> {name} ({len(png) / 1e6:.1f} MB)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user