krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

Commit 1e35a36894

1e35a36894a7d4e4f6ac9723fd24bacf08699d59

parent: 94af22ba11

Verified · cmc

cmc <hello@cleberg.net> · 2026-10-19 21:39 UTC

Messier objects from OpenNGC v20260501

scripts/make-messier.py builds SkyRenderer/Resources/messier.json:
J2000 position, type, magnitude, axes, position angle and Library page
id for 109 objects. M102 follows OpenNGC as a duplicate of M101.

Layout: unified · split

Packages/MoonriseKit/Package.swift +1 −1
@@ -22,7 +22,7 @@ let package = Package(
22 .target( 22 .target(
23 name: "SkyRenderer", 23 name: "SkyRenderer",
24 dependencies: ["SkyEngine"], 24 dependencies: ["SkyEngine"],
25 resources: [.copy("Resources/bsc5.json"), .copy("Resources/constellations.json"), .copy("Resources/coastline.json"), .copy("Resources/spacecraft.json"), .copy("Resources/skycultures.json")] 25 resources: [.copy("Resources/bsc5.json"), .copy("Resources/constellations.json"), .copy("Resources/coastline.json"), .copy("Resources/spacecraft.json"), .copy("Resources/skycultures.json"), .copy("Resources/messier.json")]
26 ), 26 ),
27 .target(name: "Feeds"), 27 .target(name: "Feeds"),
28 .target(name: "ObservingLog"), 28 .target(name: "ObservingLog"),
Packages/MoonriseKit/Sources/SkyRenderer/Resources/messier.json added +1
@@ -0,0 +1 @@
1[{"messier":1,"type":"SNR","ra":83.63321,"dec":22.01447,"magnitude":8.4,"majorAxis":8.0,"minorAxis":4.0,"page":"m1"},{"messier":2,"type":"GCl","ra":323.36254,"dec":-0.82331,"magnitude":6.25,"majorAxis":8.4,"page":"m2"},{"messier":3,"type":"GCl","ra":205.54679,"dec":28.37544,"magnitude":6.39,"majorAxis":16.2,"page":"m3"},{"messier":4,"type":"GCl","ra":245.8975,"dec":-26.52553,"magnitude":5.4,"majorAxis":28.2,"page":"m4"},{"messier":5,"type":"GCl","ra":229.64063,"dec":2.08269,"magnitude":5.95,"majorAxis":15.0,"page":"m5"},{"messier":6,"type":"OCl","ra":265.08646,"dec":-32.25417,"magnitude":4.2,"majorAxis":15.6,"page":"m6"},{"messier":7,"type":"OCl","ra":268.46325,"dec":-34.79283,"magnitude":3.3,"majorAxis":22.2,"page":"m7"},{"messier":8,"type":"Neb","ra":270.92196,"dec":-24.38017,"magnitude":5.8,"majorAxis":45.0,"minorAxis":30.0,"page":"m8"},{"messier":9,"type":"GCl","ra":259.79908,"dec":-18.51625,"magnitude":8.42,"majorAxis":6.9,"page":"m9"},{"messier":10,"type":"GCl","ra":254.28746,"dec":-4.09933,"magnitude":4.98,"majorAxis":9.3,"page":"m10"},{"messier":11,"type":"OCl","ra":282.77496,"dec":-6.27003,"magnitude":5.8,"majorAxis":9.0,"page":"m11"},{"messier":12,"type":"GCl","ra":251.8105,"dec":-1.94783,"magnitude":6.07,"majorAxis":11.1,"page":"m12"},{"messier":13,"type":"GCl","ra":250.42346,"dec":36.46131,"magnitude":5.8,"majorAxis":16.5,"page":"m13"},{"messier":14,"type":"GCl","ra":264.40067,"dec":-3.24592,"magnitude":5.73,"majorAxis":9.9,"page":"m14"},{"messier":15,"type":"GCl","ra":322.49325,"dec":12.16683,"magnitude":6.3,"majorAxis":11.1,"page":"m15"},{"messier":16,"type":"Neb","ra":274.70071,"dec":-13.80722,"magnitude":6.0,"majorAxis":120.0,"minorAxis":25.0,"page":"m16"},{"messier":17,"type":"Neb","ra":275.19629,"dec":-16.17153,"magnitude":7.0,"majorAxis":12.6,"page":"m17"},{"messier":18,"type":"OCl","ra":274.99371,"dec":-17.10197,"magnitude":6.9,"majorAxis":6.0,"page":"m18"},{"messier":19,"type":"GCl","ra":255.657,"dec":-26.26794,"magnitude":5.57,"majorAxis":7.5,"page":"m19"},{"messier":20,"type":"Neb","ra":270.67546,"dec":-22.97189,"magnitude":8.5,"majorAxis":28.0,"minorAxis":28.0,"page":"m20"},{"messier":21,"type":"OCl","ra":271.05604,"dec":-22.49006,"magnitude":5.9,"majorAxis":6.0,"page":"m21"},{"messier":22,"type":"GCl","ra":279.10083,"dec":-23.90342,"magnitude":6.17,"majorAxis":12.6,"page":"m22"},{"messier":23,"type":"OCl","ra":269.26988,"dec":-18.98533,"magnitude":5.5,"majorAxis":16.8,"page":"m23"},{"messier":24,"type":"*Ass","ra":274.23383,"dec":-18.51456,"magnitude":4.5,"majorAxis":120.0,"minorAxis":60.0,"positionAngle":90.0,"page":"m24"},{"messier":25,"type":"OCl","ra":277.94488,"dec":-19.11494,"magnitude":4.6,"majorAxis":14.1,"page":"m25"},{"messier":26,"type":"OCl","ra":281.32775,"dec":-9.38361,"magnitude":8.87,"majorAxis":6.0,"page":"m26"},{"messier":27,"type":"PN","ra":299.90158,"dec":22.72103,"magnitude":7.4,"majorAxis":6.7,"page":"m27"},{"messier":28,"type":"GCl","ra":276.13704,"dec":-24.86983,"magnitude":6.9,"majorAxis":5.1,"page":"m28"},{"messier":29,"type":"OCl","ra":305.99071,"dec":38.50767,"magnitude":6.6,"majorAxis":3.6,"page":"m29"},{"messier":30,"type":"GCl","ra":325.09175,"dec":-23.17908,"magnitude":7.1,"majorAxis":9.0,"page":"m30"},{"messier":31,"type":"G","ra":10.68479,"dec":41.26906,"magnitude":3.44,"majorAxis":177.83,"minorAxis":69.66,"positionAngle":35.0,"page":"m31"},{"messier":32,"type":"G","ra":10.67429,"dec":40.86528,"magnitude":8.13,"majorAxis":7.74,"minorAxis":4.86,"positionAngle":170.0,"page":"m32"},{"messier":33,"type":"G","ra":23.46204,"dec":30.66022,"magnitude":5.79,"majorAxis":62.09,"minorAxis":36.73,"positionAngle":23.0,"page":"m33"},{"messier":34,"type":"OCl","ra":40.53083,"dec":42.74614,"magnitude":5.2,"majorAxis":22.5,"page":"m34"},{"messier":35,"type":"OCl","ra":92.27108,"dec":24.33864,"magnitude":5.1,"majorAxis":24.0,"page":"m35"},{"messier":36,"type":"OCl","ra":84.07392,"dec":34.14075,"magnitude":6.0,"majorAxis":7.2,"page":"m36"},{"messier":37,"type":"OCl","ra":88.07646,"dec":32.553,"magnitude":5.6,"majorAxis":11.4,"page":"m37"},{"messier":38,"type":"OCl","ra":82.17704,"dec":35.85492,"magnitude":6.4,"majorAxis":9.6,"page":"m38"},{"messier":39,"type":"OCl","ra":322.95133,"dec":48.43817,"magnitude":4.6,"majorAxis":19.5,"page":"m39"},{"messier":40,"type":"**","ra":185.56708,"dec":58.08444,"magnitude":8.0,"page":"m40"},{"messier":41,"type":"OCl","ra":101.49975,"dec":-20.75422,"magnitude":4.5,"majorAxis":12.0,"page":"m41"},{"messier":42,"type":"Cl+N","ra":83.81867,"dec":-5.38967,"magnitude":4.0,"majorAxis":90.0,"minorAxis":60.0,"page":"m42"},{"messier":43,"type":"HII","ra":83.88075,"dec":-5.26747,"magnitude":9.0,"majorAxis":20.0,"minorAxis":15.0,"page":"m43"},{"messier":44,"type":"OCl","ra":130.0925,"dec":19.67206,"magnitude":3.1,"majorAxis":108.6,"page":"m44"},{"messier":45,"type":"OCl","ra":56.86917,"dec":24.10528,"magnitude":1.2,"majorAxis":150.0,"minorAxis":150.0,"positionAngle":90.0,"page":"m45"},{"messier":46,"type":"OCl","ra":115.44508,"dec":-14.81,"magnitude":6.1,"majorAxis":21.0,"page":"m46"},{"messier":47,"type":"OCl","ra":114.14592,"dec":-14.48261,"magnitude":4.4,"majorAxis":19.8,"page":"m47"},{"messier":48,"type":"OCl","ra":123.42992,"dec":-5.75044,"magnitude":5.8,"majorAxis":28.2,"page":"m48"},{"messier":49,"type":"G","ra":187.44483,"dec":8.00047,"magnitude":8.28,"majorAxis":10.21,"minorAxis":8.38,"positionAngle":156.0,"page":"m49"},{"messier":50,"type":"OCl","ra":105.66863,"dec":-8.36403,"magnitude":5.9,"majorAxis":14.1,"page":"m50"},{"messier":51,"type":"G","ra":202.46962,"dec":47.19517,"magnitude":8.36,"majorAxis":13.71,"minorAxis":11.67,"positionAngle":163.0,"page":"m51"},{"messier":52,"type":"OCl","ra":351.20167,"dec":61.59317,"magnitude":6.9,"majorAxis":9.9,"page":"m52"},{"messier":53,"type":"GCl","ra":198.23012,"dec":18.16911,"magnitude":7.79,"majorAxis":9.0,"page":"m53"},{"messier":54,"type":"GCl","ra":283.76363,"dec":-30.4785,"magnitude":7.7,"majorAxis":5.1,"page":"m54"},{"messier":55,"type":"GCl","ra":294.9975,"dec":-30.96208,"magnitude":6.49,"majorAxis":12.0,"page":"m55"},{"messier":56,"type":"GCl","ra":289.14796,"dec":30.1845,"magnitude":8.4,"majorAxis":5.8,"page":"m56"},{"messier":57,"type":"PN","ra":283.39587,"dec":33.02858,"magnitude":8.8,"majorAxis":1.27,"page":"m57"},{"messier":58,"type":"G","ra":189.43133,"dec":11.81819,"magnitude":10.3,"majorAxis":5.01,"minorAxis":3.84,"positionAngle":90.0,"page":"m58"},{"messier":59,"type":"G","ra":190.50933,"dec":11.64703,"magnitude":9.56,"majorAxis":4.55,"minorAxis":3.21,"positionAngle":165.0,"page":"m59"},{"messier":60,"type":"G","ra":190.91658,"dec":11.55269,"magnitude":8.79,"majorAxis":6.78,"minorAxis":5.45,"positionAngle":105.0,"page":"m60"},{"messier":61,"type":"G","ra":185.47875,"dec":4.47364,"magnitude":10.25,"majorAxis":6.89,"minorAxis":6.56,"positionAngle":20.0,"page":"m61"},{"messier":62,"type":"GCl","ra":255.3025,"dec":-30.11236,"magnitude":7.39,"majorAxis":7.8,"page":"m62"},{"messier":63,"type":"G","ra":198.95554,"dec":42.02928,"magnitude":8.61,"majorAxis":11.83,"minorAxis":7.16,"positionAngle":103.0,"page":"m63"},{"messier":64,"type":"G","ra":194.18183,"dec":21.68297,"magnitude":8.52,"majorAxis":10.52,"minorAxis":5.33,"positionAngle":114.0,"page":"m64"},{"messier":65,"type":"G","ra":169.733,"dec":13.09236,"magnitude":9.32,"majorAxis":7.64,"minorAxis":1.97,"positionAngle":173.0,"page":"m65"},{"messier":66,"type":"G","ra":170.06233,"dec":12.99153,"magnitude":8.92,"majorAxis":10.28,"minorAxis":4.61,"positionAngle":168.0,"page":"m66"},{"messier":67,"type":"OCl","ra":132.83388,"dec":11.81194,"magnitude":6.9,"majorAxis":33.0,"page":"m67"},{"messier":68,"type":"GCl","ra":189.86671,"dec":-26.74303,"magnitude":7.96,"majorAxis":6.6,"page":"m68"},{"messier":69,"type":"GCl","ra":277.84679,"dec":-32.34797,"magnitude":8.31,"majorAxis":5.7,"page":"m69"},{"messier":70,"type":"GCl","ra":280.80267,"dec":-32.29189,"magnitude":9.06,"majorAxis":6.6,"page":"m70"},{"messier":71,"type":"GCl","ra":298.44212,"dec":18.77839,"magnitude":6.1,"majorAxis":6.9,"page":"m71"},{"messier":72,"type":"GCl","ra":313.36629,"dec":-12.53706,"magnitude":8.96,"majorAxis":4.5,"page":"m72"},{"messier":73,"type":"Other","ra":314.73321,"dec":-12.6355,"magnitude":8.9,"page":"m73"},{"messier":74,"type":"G","ra":24.17396,"dec":15.78367,"magnitude":9.31,"majorAxis":9.89,"minorAxis":9.33,"positionAngle":87.0,"page":"m74"},{"messier":75,"type":"GCl","ra":301.52017,"dec":-21.92222,"magnitude":8.26,"majorAxis":3.6,"page":"m75"},{"messier":76,"type":"PN","ra":25.58204,"dec":51.57547,"magnitude":10.1,"majorAxis":1.12,"page":"m76"},{"messier":77,"type":"G","ra":40.66963,"dec":-0.01328,"magnitude":9.29,"majorAxis":6.11,"minorAxis":5.61,"positionAngle":12.0,"page":"m77"},{"messier":78,"type":"RfN","ra":86.69092,"dec":0.07931,"magnitude":8.0,"majorAxis":4.5,"page":"m78"},{"messier":79,"type":"GCl","ra":81.04413,"dec":-24.52422,"magnitude":8.16,"majorAxis":7.2,"page":"m79"},{"messier":80,"type":"GCl","ra":244.26046,"dec":-22.97511,"magnitude":7.3,"majorAxis":5.7,"page":"m80"},{"messier":81,"type":"G","ra":148.88821,"dec":69.06531,"magnitude":6.92,"majorAxis":21.63,"minorAxis":11.25,"positionAngle":157.0,"page":"m81"},{"messier":82,"type":"G","ra":148.96971,"dec":69.67939,"magnitude":8.3,"majorAxis":10.99,"minorAxis":5.11,"positionAngle":66.0,"page":"m82"},{"messier":83,"type":"G","ra":204.25396,"dec":-29.86542,"magnitude":7.21,"majorAxis":13.61,"minorAxis":13.21,"positionAngle":45.0,"page":"m83"},{"messier":84,"type":"G","ra":186.26558,"dec":12.88697,"magnitude":9.79,"majorAxis":7.41,"minorAxis":6.44,"positionAngle":133.0,"page":"m84"},{"messier":85,"type":"G","ra":186.35046,"dec":18.1915,"magnitude":9.05,"majorAxis":6.95,"minorAxis":5.35,"positionAngle":12.0,"page":"m85"},{"messier":86,"type":"G","ra":186.54892,"dec":12.94622,"magnitude":8.86,"majorAxis":11.53,"minorAxis":8.43,"positionAngle":128.0,"page":"m86"},{"messier":87,"type":"G","ra":187.70592,"dec":12.39111,"magnitude":9.0,"majorAxis":7.11,"minorAxis":6.67,"positionAngle":153.0,"page":"m87"},{"messier":88,"type":"G","ra":187.9965,"dec":14.42039,"magnitude":10.33,"majorAxis":8.65,"minorAxis":4.38,"positionAngle":138.0,"page":"m88"},{"messier":89,"type":"G","ra":188.91587,"dec":12.55633,"magnitude":10.08,"majorAxis":8.13,"minorAxis":8.0,"positionAngle":150.0,"page":"m89"},{"messier":90,"type":"G","ra":189.20746,"dec":13.16294,"magnitude":9.54,"majorAxis":9.12,"minorAxis":3.82,"positionAngle":22.0,"page":"m90"},{"messier":91,"type":"G","ra":188.86021,"dec":14.49633,"magnitude":10.96,"majorAxis":5.55,"minorAxis":4.52,"positionAngle":150.0,"page":"m91"},{"messier":92,"type":"GCl","ra":259.28029,"dec":43.13653,"magnitude":6.52,"majorAxis":14.4,"page":"m92"},{"messier":93,"type":"OCl","ra":116.12179,"dec":-23.85308,"magnitude":6.2,"majorAxis":15.0,"page":"m93"},{"messier":94,"type":"G","ra":192.72108,"dec":41.12044,"magnitude":8.24,"majorAxis":7.74,"minorAxis":6.68,"positionAngle":105.0,"page":"m94"},{"messier":95,"type":"G","ra":160.99042,"dec":11.70381,"magnitude":9.77,"majorAxis":7.23,"minorAxis":4.45,"positionAngle":11.0,"page":"m95"},{"messier":96,"type":"G","ra":161.69058,"dec":11.81994,"magnitude":9.21,"majorAxis":8.26,"minorAxis":5.51,"positionAngle":5.0,"page":"m96"},{"messier":97,"type":"PN","ra":168.69879,"dec":55.01903,"magnitude":9.9,"majorAxis":3.58,"page":"m97"},{"messier":98,"type":"G","ra":183.45121,"dec":14.90033,"magnitude":10.84,"majorAxis":11.04,"minorAxis":2.66,"positionAngle":152.0,"page":"m98"},{"messier":99,"type":"G","ra":184.70667,"dec":14.4165,"magnitude":9.84,"majorAxis":5.04,"minorAxis":4.74,"positionAngle":23.0,"page":"m99"},{"messier":100,"type":"G","ra":185.72846,"dec":15.82181,"magnitude":9.47,"majorAxis":6.1,"minorAxis":5.62,"positionAngle":108.0,"page":"m100"},{"messier":101,"type":"G","ra":210.80225,"dec":54.34894,"magnitude":7.9,"majorAxis":23.99,"minorAxis":23.07,"positionAngle":28.0,"page":"m101"},{"messier":103,"type":"OCl","ra":23.34088,"dec":60.658,"magnitude":7.4,"majorAxis":4.5,"page":"m103"},{"messier":104,"type":"G","ra":189.99762,"dec":-11.62306,"magnitude":8.59,"majorAxis":8.45,"minorAxis":4.91,"positionAngle":90.0,"page":"m104"},{"messier":105,"type":"G","ra":161.95663,"dec":12.58161,"magnitude":9.27,"majorAxis":4.89,"minorAxis":4.25,"positionAngle":71.0,"page":"m105"},{"messier":106,"type":"G","ra":184.73958,"dec":47.30397,"magnitude":9.29,"majorAxis":16.98,"minorAxis":7.24,"positionAngle":150.0,"page":"m106"},{"messier":107,"type":"GCl","ra":248.133,"dec":-13.05364,"magnitude":8.85,"majorAxis":7.8,"page":"m107"},{"messier":108,"type":"G","ra":167.87904,"dec":55.67411,"magnitude":10.05,"majorAxis":3.98,"minorAxis":1.66,"positionAngle":79.0,"page":"m108"},{"messier":109,"type":"G","ra":179.39992,"dec":53.37453,"magnitude":9.88,"majorAxis":8.07,"minorAxis":5.64,"positionAngle":78.0,"page":"m109"},{"messier":110,"type":"G","ra":10.092,"dec":41.68531,"magnitude":8.15,"majorAxis":16.22,"minorAxis":9.59,"positionAngle":170.0,"page":"m110"}]\ No newline at end of file
THIRD_PARTY.org +1
@@ -6,6 +6,7 @@
6| Yale Bright Star Catalogue, 5th rev. ed. (Hoffleit & Warren 1991) | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/bsc5.json= | See VizieR policy; licence statement pending | https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50 | 6| Yale Bright Star Catalogue, 5th rev. ed. (Hoffleit & Warren 1991) | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/bsc5.json= | See VizieR policy; licence statement pending | https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50 |
7| BSC5 proper motions (pmRA, pmDE) | same file, columns 6 and 7 | as above | https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50 | 7| BSC5 proper motions (pmRA, pmDE) | same file, columns 6 and 7 | as above | https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50 |
8| Stellarium sky cultures: Chinese, Indian, Mongolian, Japanese moon stations, Hawaiian starlines, Māori, Tongan, Boorong, Sámi, Norse (lines and names, converted to HR numbers by =scripts/make-skycultures.py=) | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/skycultures.json= | CC BY-SA (per culture, stated in each culture's description.md); the converted file is under the same licence | https://github.com/Stellarium/stellarium-skycultures | 8| Stellarium sky cultures: Chinese, Indian, Mongolian, Japanese moon stations, Hawaiian starlines, Māori, Tongan, Boorong, Sámi, Norse (lines and names, converted to HR numbers by =scripts/make-skycultures.py=) | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/skycultures.json= | CC BY-SA (per culture, stated in each culture's description.md); the converted file is under the same licence | https://github.com/Stellarium/stellarium-skycultures |
9| OpenNGC v20260501, Mattia Verga: Messier objects (position, type, magnitude, axes, position angle; converted by =scripts/make-messier.py=). M102 is left out: OpenNGC lists it as a duplicate of M101 | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/messier.json= | CC BY-SA 4.0; the converted file is under the same licence | https://github.com/mattiaverga/OpenNGC |
9| Hipparcos main catalogue positions (used only to match HIP to HR when building skycultures.json; not bundled) | — | ESA | https://cdsarc.cds.unistra.fr/viz-bin/cat/I/239 | 10| Hipparcos main catalogue positions (used only to match HIP to HR when building skycultures.json; not bundled) | — | ESA | https://cdsarc.cds.unistra.fr/viz-bin/cat/I/239 |
10| Global maps for globes (Mercury PIA15063; Venus Magellan C3-MDIR colourised topography; Earth Blue Marble Next Generation, December 2004; Moon SVS 4720 LROC colour 2K; Mars Viking MDIM 2.1 colour; Jupiter PIA07782; Pluto New Horizons global mosaic; Io, Europa, Ganymede, Callisto USGS Galileo/Voyager mosaics; Titan PIA22770; Enceladus Schenk 2024), downscaled to at most 2048 px | =App/Globes/*.jpg= | Public domain (NASA, USGS); credit per map in =App/Sources/GlobeMaps.swift= | https://science.nasa.gov/photojournal, https://svs.gsfc.nasa.gov/4720/, https://astrogeology.usgs.gov | 11| Global maps for globes (Mercury PIA15063; Venus Magellan C3-MDIR colourised topography; Earth Blue Marble Next Generation, December 2004; Moon SVS 4720 LROC colour 2K; Mars Viking MDIM 2.1 colour; Jupiter PIA07782; Pluto New Horizons global mosaic; Io, Europa, Ganymede, Callisto USGS Galileo/Voyager mosaics; Titan PIA22770; Enceladus Schenk 2024), downscaled to at most 2048 px | =App/Globes/*.jpg= | Public domain (NASA, USGS); credit per map in =App/Sources/GlobeMaps.swift= | https://science.nasa.gov/photojournal, https://svs.gsfc.nasa.gov/4720/, https://astrogeology.usgs.gov |
11| Sun texture for the 3D solar system (=App/Globes/sun.jpg=) | generated noise, made for this app | — | — | 12| Sun texture for the 3D solar system (=App/Globes/sun.jpg=) | generated noise, made for this app | — | — |
scripts/make-messier.py added +73
@@ -0,0 +1,73 @@
1#!/usr/bin/env python3
2"""Builds SkyRenderer/Resources/messier.json from OpenNGC (CC BY-SA 4.0), pinned to v20260501.
3
4Usage: make-messier.py
5
6Rows with a Messier number in NGC.csv and addendum.csv (M40, M45). Coordinates are J2000 degrees,
7axes arcminutes, position angle degrees east of north; magnitude is V, else B. Each object carries the
8id of its Library page (link.messier in pages/deep-sky-*.json).
9
10M102: OpenNGC lists it as a duplicate observation of M101 (addendum type "Dup"), so it is left out
11and the file has 109 objects. The Library page for M102 keeps its own account (probably NGC 5866).
12"""
13import csv
14import io
15import json
16import urllib.request
17from pathlib import Path
18
19ROOT = Path(__file__).resolve().parent.parent
20RES = ROOT / "Packages/MoonriseKit/Sources/SkyRenderer/Resources"
21PAGES = ROOT / "Packages/MoonriseKit/Sources/Library/Resources/pages"
22VERSION = "v20260501"
23BASE = f"https://raw.githubusercontent.com/mattiaverga/OpenNGC/{VERSION}/database_files"
24
25
26def fetch(url):
27 with urllib.request.urlopen(url, timeout=60) as r:
28 return r.read().decode("utf-8")
29
30
31def sexagesimal(s, hours):
32 sign = -1 if s.startswith("-") else 1
33 d, m, sec = (float(x) for x in s.lstrip("+-").split(":"))
34 v = sign * (d + m / 60 + sec / 3600)
35 return v * 15 if hours else v
36
37
38def number(s):
39 return float(s) if s.strip() else None
40
41
42def main():
43 pages = {}
44 for f in sorted(PAGES.glob("deep-sky-*.json")):
45 for p in json.loads(f.read_text()):
46 pages[p["link"]["messier"]] = p["id"]
47
48 out = {}
49 for name in ("NGC.csv", "addendum.csv"):
50 for row in csv.DictReader(io.StringIO(fetch(f"{BASE}/{name}")), delimiter=";"):
51 if not row["M"] or row["Type"] == "Dup":
52 continue
53 m = int(row["M"])
54 assert m not in out, m
55 o = {
56 "messier": m,
57 "type": row["Type"],
58 "ra": round(sexagesimal(row["RA"], True), 5),
59 "dec": round(sexagesimal(row["Dec"], False), 5),
60 "magnitude": number(row["V-Mag"]) if row["V-Mag"].strip() else number(row["B-Mag"]),
61 "majorAxis": number(row["MajAx"]),
62 "minorAxis": number(row["MinAx"]),
63 "positionAngle": number(row["PosAng"]),
64 "page": pages[m],
65 }
66 out[m] = {k: v for k, v in o.items() if v is not None}
67
68 assert sorted(out) == [m for m in range(1, 111) if m != 102], sorted(set(range(1, 111)) - set(out))
69 (RES / "messier.json").write_text(json.dumps([out[m] for m in sorted(out)], ensure_ascii=False, separators=(",", ":")))
70
71
72if __name__ == "__main__":
73 main()