v3: Sky cultures !36

merged merged by cmc on 2026-10-18 21:48 UTC · krz/moonrise:v3-sky-cultures into main

10 files changed, +220 −5

Layout: unified · split

App/Sources/SettingsScreen.swift +22 −1
@@ -1,4 +1,5 @@
1import Library 1import Library
2import SkyRenderer
2import SwiftUI 3import SwiftUI
3import UserNotifications 4import UserNotifications
4 5
@@ -13,6 +14,8 @@ enum SettingsKey {
13 /// Whether the Sky tab is currently drawn in night mode (written by the Sky tab). 14 /// Whether the Sky tab is currently drawn in night mode (written by the Sky tab).
14 static let nightActive = "nightActive" 15 static let nightActive = "nightActive"
15 static let dailySummary = "dailySummary" 16 static let dailySummary = "dailySummary"
17 /// Sky culture id, or "western" for the IAU constellations.
18 static let skyCulture = "skyCulture"
16 /// Minutes after local midnight. 19 /// Minutes after local midnight.
17 static let dailySummaryMinutes = "dailySummaryMinutes" 20 static let dailySummaryMinutes = "dailySummaryMinutes"
18} 21}
@@ -61,6 +64,7 @@ struct SettingsScreen: View {
61 @AppStorage(SettingsKey.cityLatitude) private var cityLatitude = 41.878 64 @AppStorage(SettingsKey.cityLatitude) private var cityLatitude = 41.878
62 @AppStorage(SettingsKey.cityLongitude) private var cityLongitude = -87.630 65 @AppStorage(SettingsKey.cityLongitude) private var cityLongitude = -87.630
63 @AppStorage(SettingsKey.nightMode) private var nightMode = "auto" 66 @AppStorage(SettingsKey.nightMode) private var nightMode = "auto"
67 @AppStorage(SettingsKey.skyCulture) private var skyCulture = "western"
64 @Environment(\.dismiss) private var dismiss 68 @Environment(\.dismiss) private var dismiss
65 @State private var alerts = AuroraAlerts.shared 69 @State private var alerts = AuroraAlerts.shared
66 @AppStorage(SettingsKey.dailySummary) private var dailySummary = false 70 @AppStorage(SettingsKey.dailySummary) private var dailySummary = false
@@ -117,6 +121,10 @@ struct SettingsScreen: View {
117 Text("A note each day with the Moon, the planets up that night, the best satellite pass and any events. It is worked out on this device; nothing is sent to our server.") 121 Text("A note each day with the Moon, the planets up that night, the best satellite pass and any events. It is worked out on this device; nothing is sent to our server.")
118 } 122 }
119 Section { 123 Section {
124 Picker("Constellations", selection: $skyCulture) {
125 Text("Western (IAU)").tag("western")
126 ForEach(Catalog.skyCultures) { Text($0.name).tag($0.id) }
127 }
120 Picker("Night mode", selection: $nightMode) { 128 Picker("Night mode", selection: $nightMode) {
121 Text("Off").tag("off") 129 Text("Off").tag("off")
122 Text("On").tag("on") 130 Text("On").tag("on")
@@ -125,7 +133,7 @@ struct SettingsScreen: View {
125 } header: { 133 } header: {
126 Text("Display") 134 Text("Display")
127 } footer: { 135 } footer: {
128 Text("Night mode draws the sky in red to keep your eyes adapted to the dark. After dark turns it on once the Sun is 12° below the horizon.") 136 Text("Constellations from other cultures come from Stellarium's sky cultures and are drawn over the same stars; figures with stars fainter than the app's catalogue are shortened. Night mode draws the sky in red to keep your eyes adapted to the dark. After dark turns it on once the Sun is 12° below the horizon.")
129 } 137 }
130 Section("About") { 138 Section("About") {
131 LabeledContent("Version", value: Bundle.main.versionString) 139 LabeledContent("Version", value: Bundle.main.versionString)
@@ -191,6 +199,9 @@ struct CreditsScreen: View {
191 Credit(name: "Images of the Sun", detail: "Courtesy of NASA/SDO and the AIA, EVE, and HMI science teams."), 199 Credit(name: "Images of the Sun", detail: "Courtesy of NASA/SDO and the AIA, EVE, and HMI science teams."),
192 Credit(name: "Launch schedule", detail: "The Space Devs, Launch Library 2."), 200 Credit(name: "Launch schedule", detail: "The Space Devs, Launch Library 2."),
193 Credit(name: "Deep Space Network status", detail: "NASA/JPL, DSN Now."), 201 Credit(name: "Deep Space Network status", detail: "NASA/JPL, DSN Now."),
202 Credit(name: "Satellite orbits", detail: "CelesTrak."),
203 Credit(name: "Mars rover raw images", detail: "NASA/JPL-Caltech, with MSSS and ASU for some cameras."),
204 Credit(name: "Space news headlines", detail: "Spaceflight News API, The Space Devs. Articles belong to their publishers."),
194 ] 205 ]
195 206
196 /// Every distinct image credit used by the Library, grouped by licence. 207 /// Every distinct image credit used by the Library, grouped by licence.
@@ -210,6 +221,16 @@ struct CreditsScreen: View {
210 Section("Live data") { 221 Section("Live data") {
211 ForEach(feeds, id: \.self) { row($0) } 222 ForEach(feeds, id: \.self) { row($0) }
212 } 223 }
224 Section("Sky cultures") {
225 ForEach(Catalog.skyCultures) { c in
226 Link(destination: c.source) {
227 VStack(alignment: .leading, spacing: 2) {
228 Text(c.name)
229 Text("Stellarium sky cultures. \(c.licence)").font(.footnote).foregroundStyle(.secondary)
230 }
231 }
232 }
233 }
213 ForEach(imageCredits, id: \.licence) { group in 234 ForEach(imageCredits, id: \.licence) { group in
214 Section("Library images: \(group.licence)") { 235 Section("Library images: \(group.licence)") {
215 ForEach(group.credits, id: \.self) { Text($0).font(.footnote) } 236 ForEach(group.credits, id: \.self) { Text($0).font(.footnote) }
App/Sources/SkyScreen.swift +2 −1
@@ -11,6 +11,7 @@ struct SkyScreen: View {
11 @State private var altitude = 45.0 11 @State private var altitude = 45.0
12 @State private var fieldOfView = 100.0 12 @State private var fieldOfView = 100.0
13 @AppStorage(SettingsKey.nightMode) private var nightMode = "auto" 13 @AppStorage(SettingsKey.nightMode) private var nightMode = "auto"
14 @AppStorage(SettingsKey.skyCulture) private var skyCulture = "western"
14 @State private var showSettings = false 15 @State private var showSettings = false
15 @State private var showEvents = false 16 @State private var showEvents = false
16 @State private var showLog = false 17 @State private var showLog = false
@@ -37,7 +38,7 @@ struct SkyScreen: View {
37 style.background.ignoresSafeArea() 38 style.background.ignoresSafeArea()
38 if let scene = model.scene { 39 if let scene = model.scene {
39 let view = current(width: geo.size.width) 40 let view = current(width: geo.size.width)
40 SkyView(scene: scene, centerAzimuth: view.azimuth, centerAltitude: view.altitude, fieldOfView: view.fieldOfView, style: style) { [stats] d in 41 SkyView(scene: scene, centerAzimuth: view.azimuth, centerAltitude: view.altitude, fieldOfView: view.fieldOfView, style: style, culture: Catalog.skyCultures.first { $0.id == skyCulture }) { [stats] d in
41 stats.record(d) 42 stats.record(d)
42 } 43 }
43 .ignoresSafeArea() 44 .ignoresSafeArea()
PLAN.org +3 −1
@@ -408,7 +408,9 @@ Sky, System, Sun, Launches, Library core (above).
408 408
409** v3: across time and cultures 409** v3: across time and cultures
410- Sky cultures: Chinese, Polynesian, Arabic, Norse and other 410- Sky cultures: Chinese, Polynesian, Arabic, Norse and other
411 constellations over the same stars. 411 constellations over the same stars. Done for the ten CC BY-SA cultures in
412 Stellarium's set; Arabic is not included (CC BY-ND, permission granted only
413 to Stellarium).
412- Historical sky for any date and place, with precession. Done: 3000 BCE 414- Historical sky for any date and place, with precession. Done: 3000 BCE
413 to 3000 CE, with BSC5 proper motions and the pole star of the year. 415 to 3000 CE, with BSC5 proper motions and the pole star of the year.
414- 3D planets from USGS maps; immersive sky and orrery on Vision Pro. 416- 3D planets from USGS maps; immersive sky and orrery on Vision Pro.
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")] 25 resources: [.copy("Resources/bsc5.json"), .copy("Resources/constellations.json"), .copy("Resources/coastline.json"), .copy("Resources/spacecraft.json"), .copy("Resources/skycultures.json")]
26 ), 26 ),
27 .target(name: "Feeds"), 27 .target(name: "Feeds"),
28 .target(name: "ObservingLog"), 28 .target(name: "ObservingLog"),
Packages/MoonriseKit/Sources/SkyRenderer/Resources/skycultures.json added +1
@@ -0,0 +1 @@
1[{"id":"chinese","name":"Chinese","region":"Asia","licence":"Su Song and Suzhou maps images: Public Domain Text and line: CC BY-SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/chinese","constellations":[{"name":"Net","lines":[[1409,1389,1373,1346],[1457,1411,1394,1346,1239],[1457,1479]],"native":"毕宿"},{"name":"Wall","lines":[[39,15]],"native":"壁宿"},{"name":"Three Stars","lines":[[2061,1948,1903,1852,1713],[1852,1790],[1948,2004]],"native":"参宿"},{"name":"Root","lines":[[5531,5652,5787,5685]],"native":"氐宿"},{"name":"Dipper","lines":[[6812,6913,7039,7121,7234,7194]],"native":"斗宿"},{"name":"Room","lines":[[5984,5953,5944,5928]],"native":"房宿"},{"name":"Ghosts","lines":[[3357,3366,3449,3461]],"native":"鬼宿"},{"name":"Winnowing Basket","lines":[[6746,6859,6879,6832]],"native":"箕宿"},{"name":"Horn","lines":[[5056,5107]],"native":"角宿"},{"name":"Well","lines":[[2763,2650,2529,2473],[2529,2343,2286],[2343,2421,2650],[2421,2484],[2286,2216]],"native":"井宿"},{"name":"Neck","lines":[[5315,5338,5409],[5315,5359]],"native":"亢宿"},{"name":"Legs","lines":[[271,215,231,163,165,154,226,269,337],[352,389,383,360,351,310,271]],"native":"奎宿"},{"name":"Willow","lines":[[3418,3454,3492,3482],[3410,3482],[3492,3547,3613,3665]],"native":"柳宿"},{"name":"Bond","lines":[[617,553,546]],"native":"娄宿"},{"name":"Hairy Head","lines":[[1142,1145,1151,1149],[1142,1156,1165,1178]],"native":"昴宿"},{"name":"Ox","lines":[[7776,7754,7715],[7776,7814,7830,7822],[7814,7822]],"native":"牛宿"},{"name":"Girl","lines":[[7950,7990,7982,7951]],"native":"女宿"},{"name":"Encampment","lines":[[8781,8775]],"native":"室宿"},{"name":"Supreme Palace Right Wall","lines":[[4540,4386,4399,4359,4357]],"native":"太微右垣"},{"name":"Supreme Palace Left Wall","lines":[[4689,4825,4910,4932,4968]],"native":"太微左垣"},{"name":"Heavenly Market Right Wall","lines":[[6148,6095,6008,5933,5867,5789,5854,5892,6056,6075,6175]],"native":"天市右垣"},{"name":"Heavenly Market Left Wall","lines":[[6410,6526,6623,6779,7113,7235,7141,6869,6698,6561,6378]],"native":"天市左垣"},{"name":"Rooftop","lines":[[8414,8450,8308]],"native":"危宿"},{"name":"Tail","lines":[[6241,6247,6262,6380,6553,6615,6580,6527,6508],[6247,6252]],"native":"尾宿"},{"name":"Stomach","lines":[[801,824,838]],"native":"胃宿"},{"name":"Heart","lines":[[6084,6134,6165]],"native":"心宿"},{"name":"Star","lines":[[3748,3759,3787,3845],[3748,3709,3706,3785],[3748,3785]]},{"name":"Emptiness","lines":[[8232,8131]],"native":"虚宿"},{"name":"Wings","lines":[[4287,4405],[4287,4395],[4287,4232],[4405,4514,4395],[4514,4567],[4405,4382,4488,4416,4402],[4402,4468,4529],[4395,4343,4314]],"native":"翼宿"},{"name":"Extended Net","lines":[[3903,3994,4094,3963],[3903,3963],[3903,3849],[4094,4171]],"native":"张宿"},{"name":"Chariot","lines":[[4786,4757,4662,4630],[4662,4696],[4757,4775],[4630,4623]],"native":"轸宿"},{"name":"Purple Forbidden Right Wall","lines":[[5291,4787,4434,3771,2511,1542,985]],"native":"紫微右垣"},{"name":"Purple Forbidden Left Wall","lines":[[5744,5986,6132,6396,7180,7879,8819,208]],"native":"紫微左垣"},{"name":"Turtle Beak","lines":[[1879,1876],[1879,1907]],"native":"觜宿"},{"name":"Eight Kinds of Crops","lines":[[2077,2029,1969,1678],[1969,1568],[1637,1622,2027]],"native":"八谷"},{"name":"Net for Catching Birds","lines":[[33,9098,9065],[33,9103],[33,88],[33,48]],"native":"八魁"},{"name":"Rotten Gourd","lines":[[7852,7858,7892,7883,7896]],"native":"败瓜"},{"name":"Decayed Mortar","lines":[[8353,8411,8695,8637]],"native":"败臼"},{"name":"Northern Dipper","lines":[[4301,4295,4554,4660,4905,5054,5191]],"native":"北斗"},{"name":"North River","lines":[[2990,2890,2852]],"native":"北河"},{"name":"Northern Pole","lines":[[5735,5563,5430,5321,4893]],"native":"北极"},{"name":"North Gate of the Military Camp","lines":[[8728,8728]],"native":"北落师门"},{"name":"River Turtle","lines":[[6897,7062,7188,7242,7259,7254,7226,7152,7129,6953,6951,6897]],"native":"鳖"},{"name":"Persia","lines":[[7869,7869]],"native":"波斯"},{"name":"Textile Ruler","lines":[[6730,6787]],"native":"帛度"},{"name":"Banner of Three Stars","lines":[[1556,1580,1569,1570,1544,1543,1552,1567,1601]],"native":"参旗"},{"name":"Toilet","lines":[[1865,1829,1983,2035]],"native":"厕"},{"name":"Whip","lines":[[264,264]],"native":"策"},{"name":"Royal Guards","lines":[[4915,4845,4811,4785,4728,4666,4594]],"native":"常陈"},{"name":"Big Yard for Chariots","lines":[[8699,8632,8523,8252,8047,8079,8266]],"native":"车府"},{"name":"Chariots and Cavalry","lines":[[5649,5453,5425]],"native":"车骑"},{"name":"Commodity Market","lines":[[6129,6243]],"native":"车肆"},{"name":"Hay","lines":[[708,752,666],[704,692,781]],"native":"刍藁"},{"name":"Pestle (In Winnowing Basket Mansion)","lines":[[6537,6510,6461]],"native":"杵 (箕宿)"},{"name":"Pestle (In Rooftop Mansion)","lines":[[8498,8454,8419]],"native":"杵 (危宿)"},{"name":"Changsha (Vassal of Chariot)","lines":[[4696,4696]],"native":"长沙 (附轸宿)"},{"name":"Guest House","lines":[[9013,137],[345,640,860,1035,1040,1046]],"native":"传舍"},{"name":"Retinue (In Room Mansion)","lines":[[5883,5839]],"native":"从官 (房宿)"},{"name":"Retinue (In Supreme Palace Enclosure)","lines":[[4495,4495]],"native":"从官 (太微垣)"},{"name":"Great Horn","lines":[[5340,5340]],"native":"大角"},{"name":"Chief Judge","lines":[[4646,4646]],"native":"大理"},{"name":"Mausoleum","lines":[[685,854,937,941,936,921,840,788]],"native":"大陵"},{"name":"Mattress of the Emperor","lines":[[5304,5263,5247]],"native":"帝席"},{"name":"Emperor's Seat","lines":[[6406,6406]],"native":"帝座"},{"name":"Eastern Door","lines":[[6147,6118,6104,6153]],"native":"东咸"},{"name":"Trials","lines":[[5660,5705]],"native":"顿顽"},{"name":"Send Armed Forces To Suppress (Vassal of Three Stars)","lines":[[1892,1897,1899],[1892,1903]],"native":"伐 (附参宿)"},{"name":"Punishment","lines":[[6060,6002,5954]],"native":"罚"},{"name":"Flying Fish","lines":[[3615,2736],[3615,3347],[3615,3301],[3615,2803],[3615,3024]],"native":"飞鱼"},{"name":"Tomb (Vassal of Rooftop)","lines":[[8558,8518],[8558,8597],[8558,8539],[8558,8414]],"native":"坟墓 (附危宿)"},{"name":"Axe","lines":[[8980,8998,9031]],"native":"鈇钺"},{"name":"Sickle","lines":[[433,585,565]],"native":"鈇锧"},{"name":"Basket for Mulberry Leaves","lines":[[7049,6978,6923,7125,7175,7218,7251]],"native":"扶筐"},{"name":"White Patched Nearby","lines":[[1208,872]],"native":"附白"},{"name":"Auxiliary Road","lines":[[153,153]],"native":"附路"},{"name":"Fu Yue","lines":[[6630,6630]],"native":"傅说"},{"name":"Roofing","lines":[[8402,8410]],"native":"盖屋"},{"name":"Canopy Support (Vassal of Canopy of the Emperor)","lines":[[1148,932,592],[575,480,427]],"native":"杠 (附华盖)"},{"name":"Flying Corridor","lines":[[707,542,403,343,223,193]],"native":"阁道"},{"name":"Celestial Lance","lines":[[5506,5447,5429]],"native":"梗河"},{"name":"Curved Array","lines":[[8748,285,424,6789,6322,5903]],"native":"勾陈"},{"name":"Lock (Vassal of Room)","lines":[[5993,5997],[5993,5984]],"native":"钩钤 (附房宿)"},{"name":"Dog","lines":[[7440,7362]],"native":"狗"},{"name":"Territory of Dog","lines":[[7597,7618,7650,7604,7597]],"native":"狗国"},{"name":"Coiled Thong","lines":[[5855,5778,5747,5793,5849,5889,5947,5971,5968]],"native":"贯索"},{"name":"Beacon Fire","lines":[[3191,3268,3304,3215]],"native":"爟"},{"name":"Tortoise","lines":[[6295,6462,6500,6229,6285]]},{"name":"Sea and Mountain","lines":[[4467,4520]],"native":"海山"},{"name":"Sea Rock","lines":[[3307,3699,3825,3884,3890]],"native":"海石"},{"name":"Drum at the River","lines":[[7602,7557,7525]],"native":"河鼓"},{"name":"Crane","lines":[[8425,8636,8675,8655,8848,8747,8636,8820],[8636,8787],[8636,8644,8552],[8636,8560,8486]],"native":"鹤"},{"name":"Railings","lines":[[5190,5193,5248,5285]],"native":"衡"},{"name":"Astrologer","lines":[[6556,6556]],"native":"候"},{"name":"Bow and Arrow","lines":[[2693,2827,3017,3113,3034,2949,2827,2618,2538,2773,3017]],"native":"弧矢"},{"name":"Dipper for Solids","lines":[[6281,6299,6250,6228]],"native":"斛"},{"name":"Emperor's Bodyguard","lines":[[4362,4362]],"native":"虎贲"},{"name":"Good Gourd","lines":[[7948,7906,7882,7928,7948],[7882,7871]],"native":"瓠瓜"},{"name":"Canopy of the Emperor","lines":[[427,456],[427,365],[427,336],[427,399],[427,478],[427,548]],"native":"华盖"},{"name":"Eunuch Official","lines":[[6337,6341,6355,6393]],"native":"宦者"},{"name":"Firebird","lines":[[8937,8949,8959,25,100,99,180,125,322,429]],"native":"火鸟"},{"name":"Heap of Corpses","lines":[[879,879]],"native":"积尸"},{"name":"Stored water","lines":[[1261,1261]],"native":"积水"},{"name":"Pile of Firewood","lines":[[2985,2985]],"native":"积薪"},{"name":"Group of Soldiers","lines":[[5987,5948]],"native":"积卒"},{"name":"White Patches Attached","lines":[[1744,1336]],"native":"夹白"},{"name":"Establishment","lines":[[7150,7217,7264,7304,7340,7342]],"native":"建"},{"name":"Clepsydra Terrace","lines":[[7139,7106,7178,7262]],"native":"渐台"},{"name":"Door Bolt","lines":[[6027,6027]],"native":"键闭"},{"name":"Goldfish","lines":[[1338,1465,1922,2015,2221]],"native":"金鱼"},{"name":"Recommending Virtuous Men","lines":[[4921,4921]],"native":"进贤"},{"name":"Accumulated Water","lines":[[2793,2793]],"native":"积水"},{"name":"Nine Water Wells","lines":[[8100,8100]],"native":"九坎"},{"name":"Nine Senior Officers","lines":[[4828,4847]],"native":"九卿"},{"name":"Imperial Military Flag","lines":[[1469,1520,1560,1608,1611,1549,1532,1496,1634]],"native":"九斿"},{"name":"Interpreters of Nine Dialects","lines":[[1318,1298,1383,1463,1508,1505]],"native":"九州殊口"},{"name":"Banner of Wine Shop","lines":[[3866,3782,3754]],"native":"酒旗"},{"name":"Mortar","lines":[[8522,8430,8315,8310]],"native":"臼"},{"name":"Rolled Tongue","lines":[[1135,1220,1228,1203,1131,1123]],"native":"卷舌"},{"name":"Military Well","lines":[[1696,1705,1756,1757]],"native":"军井"},{"name":"Southern Military Gate","lines":[[335,335]],"native":"军南门"},{"name":"Market for Soldiers","lines":[[2294,2443,2571,2588,2580,2387,2294]],"native":"军市"},{"name":"Chaff","lines":[[6492,6492]],"native":"糠"},{"name":"Boats and Lake","lines":[[5323,5365]],"native":"亢池"},{"name":"Peafowl","lines":[[6582,6745,6916,7074,7107,7665,7913,6982,7590,8181,7790]],"native":"孔雀"},{"name":"Crying","lines":[[8351,8452]],"native":"哭"},{"name":"Arsenal","lines":[[5231,5440,5288,5192,5089,4940,4819,4802]],"native":"库楼"},{"name":"Captain of the Bodyguards","lines":[[4883,4883]],"native":"郎将"},{"name":"Officers of the Imperial Guard","lines":[[4737,4733,4738,4752,4717,4707,4766,4753,4667,4789,4815,4756,4643,4602]],"native":"郎位"},{"name":"Old Man","lines":[[2326,2326]],"native":"老人"},{"name":"Thunder and Lightning","lines":[[8923,8893,8795,8697,8665,8634]],"native":"雷电"},{"name":"Line of Ramparts","lines":[[9087,9067,9089,3,9087],[9067,8834,8698,8573,8418,8322,8278,8260,8288,8322]],"native":"垒壁阵"},{"name":"Resting Palace (Vassal of Encampment)","lines":[[8667,8684],[8641,8650],[8880,8905],[8775,8684],[8775,8650],[8775,8880]],"native":"离宫 (附室宿)"},{"name":"Jade Ornament on Ladies' Wear","lines":[[8135,8214]],"native":"离瑜"},{"name":"Pearls on Ladies' Wear","lines":[[7897,7884,7873,7831]],"native":"离珠"},{"name":"Whetstone","lines":[[1269,1287,1369,1348]],"native":"砺石"},{"name":"Jewel Market","lines":[[6093,6149]],"native":"列肆"},{"name":"Astronomical Observatory","lines":[[4310,4294,4291]],"native":"灵台"},{"name":"Six Jia","lines":[[2401,2527,3082,2742,1523,1686]],"native":"六甲"},{"name":"Network of Dykes","lines":[[7889,7900,7937]],"native":"罗堰"},{"name":"Horse's Abdomen","lines":[[5267,5267]],"native":"马腹"},{"name":"Horse's Tail","lines":[[4732,4638,4621]],"native":"马尾"},{"name":"Bee","lines":[[4844,4798]],"native":"蜜蜂"},{"name":"The Hall of Glory","lines":[[4418,4471,4432]],"native":"明堂"},{"name":"Southern Boat","lines":[[4050,4140,4199,4037,3685]],"native":"南船"},{"name":"South River","lines":[[2828,2845,2943]],"native":"南河"},{"name":"Southern Gate","lines":[[5132,5460]],"native":"南门"},{"name":"Inner Kitchen","lines":[[4863,4916]],"native":"内厨"},{"name":"Inner Steps","lines":[[3323,3648,3531],[3757,3505,3660]],"native":"内阶"},{"name":"High Judge","lines":[[4014,3974,3857]],"native":"内平"},{"name":"Inner Screen","lines":[[4515,4517,4589,4608]],"native":"内屏"},{"name":"Imperial Passageway","lines":[[7157,7298,7314,7395,7534]],"native":"辇道"},{"name":"Bird's Beak","lines":[[8502,8540,9060,126,187,77,9076]],"native":"鸟喙"},{"name":"Peasant","lines":[[7029,7029]],"native":"农丈人"},{"name":"Woman's Bed","lines":[[6418,6436,6485]],"native":"女床"},{"name":"Female Protocol","lines":[[6725,6725]],"native":"女史"},{"name":"Thunderbolt","lines":[[8773,8852,8916,8969,9072]],"native":"霹雳"},{"name":"Judging","lines":[[5020,5287]]},{"name":"Flat Road","lines":[[4963,5150]],"native":"平道"},{"name":"Screen","lines":[[1702,1654]],"native":"屏"},{"name":"Seven Excellencies","lines":[[6200,6092,6023,5914,5763,5733,5681]],"native":"七公"},{"name":"Imperial Guards","lines":[[5776,5695,5576,5571,5626,5708,5683,5605,5528,5469]],"native":"骑官"},{"name":"Chariots and Cavalry General","lines":[[5646,5646]],"native":"骑阵将军"},{"name":"Weeping","lines":[[8512,8499]],"native":"泣"},{"name":"Green Hill","lines":[[4552,4565,4444,4449,4450,4458,4494]],"native":"青丘"},{"name":"Palace Gate","lines":[[2707,2506]],"native":"阙丘"},{"name":"Humans","lines":[[8225,8173,8313,8321]],"native":"人"},{"name":"Solar Star","lines":[[5838,5838]],"native":"日"},{"name":"Three Excellencies (In Supreme Palace Enclosure)","lines":[[4829,4858]],"native":"三公 (太微垣)"},{"name":"Three Excellencies (In Purple Forbidden Enclosure)","lines":[[5112,5023]],"native":"三公 (紫微垣)"},{"name":"Triangle","lines":[[5671,5897,6217,5671]],"native":"三角形"},{"name":"Three Top Instructors","lines":[[3576,3609]],"native":"三师"},{"name":"Three Steps","lines":[[3569,3594,4033,4069,4377]],"native":"三台"},{"name":"Royal Secretary","lines":[[6566,6161],[6566,6223],[6566,6126],[6566,6315]],"native":"尚书"},{"name":"Junior Officers","lines":[[4259,4192,4208]],"native":"少微"},{"name":"Snake's Abdomen","lines":[[837,806,705,570]],"native":"蛇腹"},{"name":"Snake's Head","lines":[[591,1175]],"native":"蛇首"},{"name":"Snake's Tail","lines":[[98,8471,8254,8021]],"native":"蛇尾"},{"name":"Changing Room (Vassal of Tail)","lines":[[6260,6260]],"native":"神宫 (附尾宿)"},{"name":"Twelve States","lines":[[8075,8137]],"native":"十二国"},{"name":"Cross","lines":[[4763,4730],[4853,4656]],"native":"十字架"},{"name":"Excrement","lines":[[1996,1996]],"native":"屎"},{"name":"Municipal Office","lines":[[6567,6581],[6734,6446],[6496,6601]],"native":"市楼"},{"name":"Eunuch","lines":[[4137,4124,4203,4247]],"native":"势"},{"name":"Official for Irrigation","lines":[[2159,2199,2223,2198]],"native":"水府"},{"name":"Crooked Running Water","lines":[[472,338,191]],"native":"水委"},{"name":"Water Level","lines":[[2864,3008,3163,3208]],"native":"水位"},{"name":"Deified Judge of Right and Wrong","lines":[[8097,8123]],"native":"司非"},{"name":"Deity in Charge of Monsters","lines":[[2084,2134,2135,2047]],"native":"司怪"},{"name":"Deified Judge of Rank","lines":[[8328,8277]],"native":"司禄"},{"name":"Deified Judge of Disaster and Good Fortune","lines":[[8178,8163]],"native":"司危"},{"name":"Four Channels","lines":[[2649,2503,2385,2298]],"native":"四渎"},{"name":"Four Advisors","lines":[[4084,4062]],"native":"四辅"},{"name":"Grandson","lines":[[2256,2177]],"native":"孙"},{"name":"Guard of the Sun","lines":[[4518,4518]],"native":"太阳守"},{"name":"First Great One","lines":[[5162,5162]],"native":"太乙"},{"name":"Crown Prince","lines":[[4527,4527]],"native":"太子"},{"name":"Royals","lines":[[4335,4335]],"native":"太尊"},{"name":"Flying Serpent","lines":[[8585,8541,8335,8301,8281,8371,8494,8538,9071,9045,9008,8926,8613,8780,8830,8860,8961,9003,8976,8965]],"native":"螣蛇"},{"name":"Celestial Concave","lines":[[1012,1012]],"native":"天阿"},{"name":"Celestial Flail","lines":[[6688,6555,6536,6705,6588]],"native":"天棓"},{"name":"Market Officer","lines":[[6973,7020,7032,7063,7149,7193,7236,7225,7209]],"native":"天弁"},{"name":"Square Celestial Granary","lines":[[74,334,402,539,509,583]],"native":"天仓"},{"name":"Celestial Slander","lines":[[1177,1177]],"native":"天谗"},{"name":"Celestial Kitchen","lines":[[7310,7462,7582,7685,7676,7371]],"native":"天厨"},{"name":"Celestial Boat","lines":[[834,915,1017,1087,1122,1273,1303,1330,1314]],"native":"天船"},{"name":"Celestial Bed","lines":[[5334,5691,5886],[6069,5589]],"native":"天床"},{"name":"Great General of Heaven","lines":[[603,496,464,430,469,458,477,557,622,664,660,603]],"native":"天大将军"},{"name":"Celestial Drumstick","lines":[[7710,7667,7596,7570]],"native":"天桴"},{"name":"Celestial Spokes","lines":[[5794,5812]],"native":"天辐"},{"name":"Materials for Making Tents","lines":[[8720,8720]],"native":"天纲"},{"name":"Celestial High Terrace","lines":[[1620,1547],[1739,1620]],"native":"天高"},{"name":"Celestial Hook","lines":[[7850,7957,8162,8417,8561,8694,8872]],"native":"天钩"},{"name":"Celestial Dog","lines":[[3426,3477,3438,3468,3518,3556]],"native":"天狗"},{"name":"Celestial Pass","lines":[[1910,1910]],"native":"天关"},{"name":"Great Emperor of Heaven","lines":[[8546,8546]],"native":"天皇大帝"},{"name":"Celestial Pier","lines":[[1740,1805],[1740,1773],[1740,1689],[1740,1706]],"native":"天潢"},{"name":"Celestial Pigsty","lines":[[255,267,203,194]],"native":"天溷"},{"name":"Celestial Cock","lines":[[7489,7515]],"native":"天鸡"},{"name":"Judge for Estimating the Age of Animals","lines":[[3634,3634]],"native":"天记"},{"name":"Celestial Discipline","lines":[[6103,6212,6324,6332,6346,6431,6570,6695]],"native":"天纪"},{"name":"Celestial River","lines":[[6402,6453,6486]],"native":"天江"},{"name":"Celestial Street","lines":[[1388,1329]],"native":"天街"},{"name":"Celestial Tally","lines":[[1396,1444],[1396,1351,1414,1473,1484,1458,1381]],"native":"天节"},{"name":"Celestial Ford","lines":[[8115,8146,8130,8028,7924,7730,7528,7796,7949,8115]],"native":"天津"},{"name":"Celestial Stable","lines":[[63,82,68]],"native":"天厩"},{"name":"Celestial Wolf","lines":[[2491,2491]],"native":"天狼"},{"name":"Celestial Prison","lines":[[4248,4422],[4277,4431],[4288,4392]],"native":"天牢"},{"name":"Celestial Ramparts","lines":[[8264,8311,8318,8319],[8187,8128],[8175,8195]],"native":"天垒城"},{"name":"Judge for Nobility","lines":[[4407,4566,4569]],"native":"天理"},{"name":"Celestial Foodstuff","lines":[[1066,1061,1038,1030]],"native":"天廪"},{"name":"Celestial Gate","lines":[[4981,5068]],"native":"天门"},{"name":"Celestial Money","lines":[[8405,8326,8305,8431,8447,8405]],"native":"天钱"},{"name":"Celestial Spear","lines":[[5328,5350,5404]],"native":"天枪"},{"name":"Circular Celestial Granary","lines":[[911,1007,896,813,649,718,754,804,779,739,691,639,650]],"native":"天囷"},{"name":"Celestial Milk","lines":[[5881,5881]],"native":"天乳"},{"name":"Celestial Earth God's Temple","lines":[[3207,3445,3485,3734,3803]],"native":"天社"},{"name":"Celestial Farmland (In Horn Mansion)","lines":[[5264,5105]],"native":"天田 (角宿)"},{"name":"Celestial Farmland (In Ox Mansion)","lines":[[8110,7980],[8110,8080,7936],[7980,7936]],"native":"天田 (牛宿)"},{"name":"Celestial Premier","lines":[[3989,4042]],"native":"天相"},{"name":"Celestial Great One","lines":[[5226,5226]],"native":"天乙"},{"name":"Celestial Yin Force","lines":[[1015,972,951]],"native":"天阴"},{"name":"Ricks of Grain","lines":[[749,749]],"native":"天庾"},{"name":"Celestial Spring","lines":[[7343,7337,7348]],"native":"天渊"},{"name":"Celestial Orchard","lines":[[440,566,674,721,789,897,1143,1190,1195,1347,1393,1464,1453]],"native":"天园"},{"name":"Celestial Meadows","lines":[[1231,1162,1136,1084,984,874,811,818,850,919,1003,1088,1173,1181,1213,1240]],"native":"天苑"},{"name":"Celestial Keyhole","lines":[[6672,6595],[6545,6519,6520,6616]],"native":"天籥"},{"name":"Celestial Pillar","lines":[[8002,8112,7686,7312,6809]],"native":"天柱"},{"name":"Celestial Wine Cup","lines":[[2808,2777,2630]],"native":"天樽"},{"name":"Butcher's Shops","lines":[[6895,6765]],"native":"屠肆"},{"name":"Official for Materials Supply","lines":[[8520,8562]],"native":"土公吏"},{"name":"Master of Constructions","lines":[[188,188]],"native":"土司空"},{"name":"Outer Kitchen","lines":[[3321,3459,3500]],"native":"外厨"},{"name":"Outer Fence","lines":[[595,549,489,434,361,294,224]],"native":"外屏"},{"name":"Wang Liang","lines":[[21,130,219,168,123]],"native":"王良"},{"name":"Administrative Center","lines":[[3888,3894,3775,3619,3662]],"native":"文昌"},{"name":"Five Chariots","lines":[[1577,1708,2088,2095,1791]],"native":"五车"},{"name":"Interior Seats of the Five Emperors","lines":[[881,581],[881,774],[881,961],[881,1230]],"native":"五帝内座"},{"name":"Seats of the Five Emperors","lines":[[4534,4535],[4534,4564],[4534,4543]],"native":"五帝座"},{"name":"Five Feudal Kings","lines":[[2540,2697,2821,2905,3067]],"native":"五诸侯"},{"name":"Five Lords","lines":[[4946,4920,4851,4663]],"native":"五诸侯"},{"name":"Western Door","lines":[[5941,5908,5848]],"native":"西咸"},{"name":"Xi Zhong","lines":[[7328,7420,7469,7503]],"native":"奚仲"},{"name":"Pool of Harmony","lines":[[1749,1825,1729]],"native":"咸池"},{"name":"Prime Minister","lines":[[4716,4716]],"native":"相"},{"name":"Little Dipper","lines":[[4674,4583,4174,4234,3860,3795,3543,3340,3000]],"native":"小斗"},{"name":"Temple","lines":[[8504,8533,8610,8727]],"native":"虚梁"},{"name":"Xuanyuan","lines":[[3579,3612,3690,3705],[3861,3731,3773,3873,3905,4031,4057,3975,3982,3852],[3982,4133],[3982,3980]],"native":"轩辕"},{"name":"Sombre Lance","lines":[[5351,5351]],"native":"玄戈"},{"name":"Battle Axe (Vassal of Well)","lines":[[2216,2216]],"native":"钺 (附井宿)"},{"name":"Gate of Yang","lines":[[5471,5485]],"native":"阳门"},{"name":"Wild Cockerel","lines":[[2429,2429]],"native":"野鸡"},{"name":"Usher to the Court","lines":[[4695,4695]],"native":"谒者"},{"name":"Exotic Bird","lines":[[6417,6411,6163,6102,5339,6020,5303],[6020,5470,5336]],"native":"异雀"},{"name":"Official in Charge of Pasturing","lines":[[413,437,463,510]],"native":"右更"},{"name":"Right Flag","lines":[[7429,7474,7377,7387,7447],[7460,7446,7584]],"native":"右旗"},{"name":"Right Conductor","lines":[[5235,5185,5200]],"native":"右摄提"},{"name":"Fish","lines":[[6662,6662]],"native":"鱼"},{"name":"Palace Guard","lines":[[8396,8439,8480],[8516,8529,8478],[8628,8693],[8592,8670],[8567,8534,8508],[8676,8704,8679],[8673,8709,8711],[8812,8817,8789],[8939,8932,8906],[8892,8890],[8865,8858,8841],[8850,8968,8988]],"native":"羽林军"},{"name":"Jade Well","lines":[[1679,1617,1666,1735]],"native":"玉井"},{"name":"Maids-in-waiting","lines":[[7352,7124],[7352,6927]],"native":"御女"},{"name":"Lunar Star","lines":[[1256,1256]],"native":"月"},{"name":"Whisper (Vassal of Net)","lines":[[1479,1479]],"native":"附耳 (附毕宿)"},{"name":"Cloud and Rain","lines":[[9022,8984]],"native":"云雨"},{"name":"Zaofu","lines":[[8571,8465,8469,8316,8334]],"native":"造父"},{"name":"Long Wall","lines":[[4127,4209,4227,4146]],"native":"长垣"},{"name":"Grandfather","lines":[[1956,1862]],"native":"丈人"},{"name":"Twinkling Indicator","lines":[[5435,5435]],"native":"招摇"},{"name":"Assistant (Vassal of Northern Dipper)","lines":[[5062,5062]],"native":"辅 (附北斗)"},{"name":"Executions","lines":[[5484,5548,5603]],"native":"折威"},{"name":"Battle Chariots","lines":[[5526,5591,5686]],"native":"阵车"},{"name":"Right linchpin (Vassal of Chariot)","lines":[[4623,4623]],"native":"右辖 (附轸宿)"},{"name":"Weaving Girl","lines":[[7051,7001,7056]],"native":"织女"},{"name":"Left linchpin (Vassal of Chariot)","lines":[[4775,4775]],"native":"左辖 (附轸宿)"},{"name":"Tripod of the Zhou","lines":[[4983,4924,4954]],"native":"周鼎"},{"name":"Feudal Kings","lines":[[2034,1928,1821,1659,1586,1497]],"native":"诸王"},{"name":"Pillars (In Net Mansion)","lines":[[1605,1612,1641,1605],[2011,2012,1995,2011],[1843,1914]],"native":"柱 (毕宿)"},{"name":"Pillars (In Horn Mansion)","lines":[[5378,5367],[5260,5249],[5354,5395],[5221,5210,5168],[5028,5028]],"native":"柱 (角宿)"},{"name":"Official of Royal Archives","lines":[[6920,6920]],"native":"柱史"},{"name":"Son","lines":[[2056,2040]],"native":"子"},{"name":"Patriarchal Clan","lines":[[7061,7069]],"native":"宗"},{"name":"Official of Religious Ceremonies","lines":[[6712,6714,6723,6752]],"native":"宗人"},{"name":"Official for the Royal Clan","lines":[[6603,6629]],"native":"宗正"},{"name":"Official in Charge of the Forest","lines":[[773,793,809,847,836]],"native":"左更"},{"name":"Left Flag","lines":[[7479,7488,7536,7546,7635,7645,7622,7664,7724]],"native":"左旗"},{"name":"Left Conductor","lines":[[5502,5475,5477]],"native":"左摄提"},{"name":"Seat Flags","lines":[[2487,2585,2459,2483,2427,2516,2420,2419,2539]],"native":"座旗"}]},{"id":"indian","name":"Indian (Vedic)","region":"Asia","licence":"CC BY-SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/indian","constellations":[{"name":"Twin physicians of Gods","lines":[[546,553]]},{"name":"She who bears","lines":[[801,824,838]]},{"name":"Nurses of kartiikeya","lines":[[1180,1178,1165,1156,1142,1145,1149,1151]]},{"name":"The spirited one","lines":[[1457,1457]]},{"name":"The deer's head","lines":[[1879,1876,1907]]},{"name":"The moist one","lines":[[2061,2061]]},{"name":"The restorers of goods","lines":[[2890,2990]]},{"name":"The nourisher","lines":[[3461,3357]]},{"name":"The embrace","lines":[[3482,3410,3418,3454,3492]]},{"name":"The bountiful one","lines":[[3982,3982]]},{"name":"The former red one","lines":[[4357,4359]]},{"name":"The latter red one","lines":[[4534,4534]]},{"name":"The hand","lines":[[4757,4786],[4757,4662,4630,4623],[4630,4786]]},{"name":"The bright one","lines":[[5056,5056]]},{"name":"The honest one","lines":[[5340,5340]]},{"name":"The gifted one","lines":[[5787,5685,5531],[5685,5652]]},{"name":"Followers of Radha","lines":[[5984,5953,5944]]},{"name":"The eldest one","lines":[[6084,6134,6165]]},{"name":"The root","lines":[[6241,6252,6271,6380,6553,6615,6580,6527,6508]]},{"name":"The former undefeated one","lines":[[6879,6859]]},{"name":"The latter undefeated one","lines":[[7121,7194]]},{"name":"The Ear","lines":[[7602,7557,7525]]},{"name":"The most famous one","lines":[[7928,7906]]},{"name":"hundred cures","lines":[[8518,8518]]},{"name":"The first of the blessed feet","lines":[[8775,8781]]},{"name":"The prosperous one","lines":[[361,361]]},{"name":"The invincible one","lines":[[7054,7001,7056]]}]},{"id":"mongolian","name":"Mongolian","region":"Asia","licence":"CC BY-SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/mongolian","constellations":[{"name":"The Bow and the Arrow","lines":[[7328,7420,7528,7796,7924],[7796,7949,8115,8310],[7796,7615,7417]]},{"name":"The Five Stars Woman","lines":[[542,403,264,168,21]]},{"name":"The Seven Buddhas","lines":[[5191,5054,4905,4660,4554,4295,4301,4660]]},{"name":"The Three Stags","lines":[[7924,7001,7557,7924]]}]},{"id":"japanese_moon_stations","name":"Japanese moon stations","region":"Asia","licence":"Text and data: CC BY-SA Yasui's Map detail[4] used with authorisation from Steven Renshaw","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/japanese_moon_stations","constellations":[{"name":"Horn","lines":[[5056,5107]]},{"name":"Neck","lines":[[5359,5315,5338,5409]]},{"name":"Root","lines":[[5652,5531,5685,5787,5652]]},{"name":"Chamber","lines":[[5984,5953,5944,5928]]},{"name":"Heart","lines":[[6084,6134,6165]]},{"name":"Tail","lines":[[6241,6247,6271,6380,6553,6615,6580,6527,6508]]},{"name":"Basket","lines":[[6859,6746,6832,6879,6859]]},{"name":"Dipper","lines":[[7234,7121,7039,7194]]},{"name":"Cow","lines":[[7754,7776,7822,7814]]},{"name":"Woman","lines":[[7950,7990,7985,7951,7950]]},{"name":"Emptiness","lines":[[8232,8131]]},{"name":"Roof Top","lines":[[8414,8308,8450,8414]]},{"name":"Room","lines":[[8781,8775]]},{"name":"Wall","lines":[[39,15]]},{"name":"Stride","lines":[[351,271,215,163,165,154,226,269,337,352,383,360,351]]},{"name":"Hill","lines":[[546,553,617]]},{"name":"Stomach","lines":[[838,801,824,838]]},{"name":"Stopping Place","lines":[[1142,1145,1165,1156,1142]]},{"name":"Net","lines":[[1409,1373],[1346,1412,1457]]},{"name":"Turtle Snout","lines":[[1907,1876,1879,1907]]},{"name":"Investigator","lines":[[2061,1790,1852,1903,1948],[1852,1713]]},{"name":"Well","lines":[[2286,2343,2421,2484],[2473,2529,2650,2763],[2529,2343],[2650,2421]]},{"name":"Ogre","lines":[[3461,3357,3366,3449,3461]]},{"name":"Willow","lines":[[3418,3410,3482,3547,3613,3665],[3482,3454]]},{"name":"Stars","lines":[[3845,3787,3759,3748,3709,3706]]},{"name":"Stretched Net","lines":[[3849,3903,3970,3994,4094,4171]]},{"name":"Wings","lines":[[4232,4287,4382,4468,4402,4305],[4314,4343,4514,4405]]},{"name":"Chariot Cross-Board","lines":[[4630,4662,4757,4786,4630]]}]},{"id":"hawaiian_starlines","name":"Hawaiian","region":"Oceania","licence":"CC BY-SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/hawaiian_starlines","constellations":[{"name":"The Canoe Bailer of Makali`i","lines":[[2491,2943,2990,2890,2088,2095,1791,1577,1708,2088],[2491,2294,2326]],"native":"Ke Ka o Makali’i"},{"name":"The Cat's Cradle","lines":[[2061,1948,2004,1713,1852,1790,2061],[1948,1903,1852]],"native":"Ka Hei-Hei o Na Keiki"},{"name":"The Chief's Eyes","lines":[[1142,1140]],"native":"Makali`i"},{"name":"Voice of Joy","lines":[[4757,4662,4630,4786,4757]],"native":"Me`e"},{"name":"The Seven","lines":[[5191,5054,4905,4660,4301,4295,4554,4660]],"native":"Na Hiku"},{"name":"The Backbone","lines":[[424,4301],[5191,5340,5056],[4757,5056],[4699,4763]],"native":"Ka Iwikuamo’o"},{"name":"The Chief's Fishline","lines":[[6527,6580,6615,6553,6380,6262,6247,6241,6165,6134,6084,5953],[5984,5953,5944]],"native":"Manaiakalani"},{"name":"Navigator's Triangle","lines":[[7924,7557,7001,7924],[7417,7528,7796,7949,7924,7796]]},{"name":"Kite of Kawelo","lines":[[15,8775,8781,39,15],[8728,8781],[21,15],[188,99],[39,188],[8775,8688]],"native":"Ka Lupe o Kawelo"},{"name":"Chief Frigate Bird","lines":[[542,403,264,168,21]],"native":"`Iwa Keli`i"},{"name":"Cared for by Moon","lines":[[4763,4730],[4853,4656]],"native":"Hanaiakamalama"},{"name":"The Pointers","lines":[[5460,5267]],"native":"Nakuhikuhi"},{"name":"Leo","lines":[[4534,4357,4359,3982,3975,4057,4031,3905,3873],[4534,4359]],"native":"Ka`ohiweliweli"}]},{"id":"maori","name":"Māori","region":"Oceania","licence":"Text and lines: CC BY-SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/maori","constellations":[{"name":"Taki-o-Autahi","lines":[[4763,4730],[4853,4656]]},{"name":"Te Taura-o-te-waka-o-tamarereti","lines":[[5267,5460]]},{"name":"Tau-toro","lines":[[1948,1903,1852]]},{"name":"The Great Boat of Tama Rereti","lines":[[6241,6247,6262,6380,6553,6615,6580,6527,6508]]},{"name":"Matakarehu","lines":[[1457,1412,1346,1373,1409]]},{"name":"The Sail of Tainui","lines":[[1457,1412,1346,1373,1409],[1457,1409],[1346,1852],[1142,1346],[1948,1903,1852]]}]},{"id":"tongan","name":"Tongan","region":"Oceania","licence":"CC BY-SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/tongan","constellations":[{"name":"Southern Wild Duck","lines":[[4763,4730],[4853,4656]],"native":"Toloatonga"},{"name":"Two Men","lines":[[2890,2990]],"native":"Lua tangata"},{"name":"Wild Duck","lines":[[1948,1903,1852]],"native":"Toloa"},{"name":"Pigeon Perch","lines":[[1457,1411,1346,1373,1409]],"native":"Tu'ulalupe"},{"name":"Big Wild Duck","lines":[[3485,3699],[3307,3734]],"native":"Toloalahi"},{"name":"Mound Top","lines":[[5460,5267]],"native":"Fungasia"},{"name":"String of fishes","lines":[[1788,1852,1903,1948,1931,1900,1892]],"native":"Tuinga ika"},{"name":"Wild Duck's Point of Land","lines":[[1903,3696,4739]],"native":"Houmatoloa"},{"name":"Two Carriers","lines":[[4737,4983,4968]],"native":"Fatanalua"},{"name":"Cloud of 'Uvea","lines":[[5971,5947,5889,5849,5793,5747,5778]],"native":"ʻAo ʻo ʻUvea"},{"name":"Wing of Tafahi","lines":[[542,403,264,168,21]],"native":"Kapakau'o'tafahi"}]},{"id":"boorong","name":"Boorong (Australia)","region":"Oceania","licence":"Data and illustrations: CC-BY SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/boorong","constellations":[{"name":"Long-necked tortoise","lines":[[2985,3067],[2985,2905],[3067,2973],[2905,2973,2857,2816]],"native":"Wanjel"},{"name":"Fan-tailed cockatoo","lines":[[2852,2890],[2896,2890,2924],[2890,3040],[2924,2930,2962,3013,3040]],"native":"Yurree"},{"name":"Two teenage boys","lines":[[1963,1955,1948],[1955,1988,2024,2037],[1852,1834],[1948,1931],[1873,1868,1861,1863]],"native":"Kulkunbulla"},{"name":"Girls","lines":[[1151,1145,1140,1142,1156,1165,1180,1178,1183,1172]],"native":"Lamankurrk"},{"name":"Red kangaroo","lines":[[1203,1228,1306,1454],[1605,1494,1306],[1494,1708],[1738,1729],[1641,1738,1729,1760],[1738,1760],[1641,1612,1454,1306],[1605,1824],[1760,1824]],"native":"Purra"},{"name":"Ring-tail possum","lines":[[4744,4730,4656,4763],[4754,4749,4702,4706,4739,4768,4823,4853,4897]],"native":"Bunya"},{"name":"Red-rumped parrot","lines":[[5984,5953,5944,5928],[6165,6134,6084,5824]],"native":"Djuit"},{"name":"Crow","lines":[[2515,2435,2326,2281,2290]],"native":"War"},{"name":"Female crow, wife of War","lines":[[4114,4177,4198],[4239,4257,4337]],"native":"Collowgulloric War"},{"name":"Owlet nightjar","lines":[[468,460,472]],"native":"Yerredetkurrk"},{"name":"Needlewood hakea and willie wagtails","lines":[[4983,4954,4883,4869,4738,4707],[4983,4984,4949,4792]],"native":"Tourtchinboiongerra"},{"name":"Red-kneed dotterel","lines":[[5593,5482,5463],[5482,5546],[5482,5349],[5482,5460]],"native":"Berm-Berm-Gle"},{"name":"Australian kestrel","lines":[[6527,6580],[6508,6580,6615],[6580,6523,6380],[6580,6832]],"native":"Karik Karik"},{"name":"Mallee fowl","lines":[[7001,7056],[7054,7056],[7054,7139,7140,7162,7178,7192,7102,7106,7043,7056]],"native":"Neilloan"},{"name":"Jacky lizard","lines":[[2580,2607,2616,2640],[2686,2704,2718,2745,2749,2750,2734,2724,2708,2688],[2614,2611],[2614,2567],[2611,2646,2637,2611]],"native":"Unurgunite"},{"name":"Female brolga","lines":[[98,98]],"native":"Kourt-chin"},{"name":"Male brolga","lines":[[1010,1083,1175,1744,2736,2662,2803],[1744,1922],[1744,1208]],"native":"Kourt-chin"},{"name":"Great fish","lines":[[7871,7918,7928,7948]],"native":"Otchocut"},{"name":"Wedge tailed eagle","lines":[[2294,2429,2491,2596,2657]],"native":"Warepil"},{"name":"Female eagle, wife of Warepil","lines":[[1560,1617,1679,1696,1705,1702],[1679,1713]],"native":"Collowgulloric Warepil"},{"name":"Emu","lines":[[5649,5649]],"native":"Tchingal"},{"name":"Purple crowned lorikeet","lines":[[7429,7519,7595,7648,7693],[7525,7557,7602]],"native":"Totyarguil"},{"name":"Meat ant","lines":[[5200,5185,5235,5340,5475,5477],[5340,5506],[5340,5429],[5506,5681],[5429,5602,5681]],"native":"Marpeankurrk"},{"name":"Galaxy","lines":[[6859,6859]],"native":"Warring"},{"name":"Singing bushlark","lines":[[5201,5201]],"native":"Weetkurrk"},{"name":"Pink cockatoo","lines":[[1457,1457]],"native":"Gellarlec"},{"name":"Species of ant","lines":[[7754,7754]],"native":"Collenbitchick"},{"name":"Boomerang","lines":[[7122,7188,7242,7259,7254,7226,7152,7021]],"native":"Won"},{"name":"The maned snake","lines":[[3634,3634]],"native":"Mindi"}]},{"id":"sami","name":"Sámi","region":"Europe","licence":"CC BY-SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/sami","constellations":[{"name":"Sarva the Elk","lines":[[542,403,264,168,21],[403,915,1017,936],[1017,1122,1220,1203],[1122,1708,1791],[1708,1641,1577]]},{"name":"Favdna's Bow and Arrow","lines":[[5191,5054,4905,4660,4295],[4301,4554]]},{"name":"Galla's Sons","lines":[[1948,1903,1852]]},{"name":"Sky Support","lines":[[424,424]]},{"name":"Favdna","lines":[[5340,5340]]},{"name":"The Runner","lines":[[7001,7001]]},{"name":"Morning Star","lines":[[7557,7557]]},{"name":"Pack of Dogs","lines":[[1165,1165]]},{"name":"Galla","lines":[[2943,2943]]},{"name":"The Ski Runners","lines":[[2890,2890],[2990,2990]]}]},{"id":"norse","name":"Norse","region":"Europe","licence":"CC BY-SA","source":"https://github.com/Stellarium/stellarium-skycultures/tree/master/norse","constellations":[{"name":"Aurvandil's Toe","lines":[[5778,5747,5793,5849,5889,5947,5971]]},{"name":"Wolf's Mouth","lines":[[1457,1412,1346,1373,1409]]},{"name":"The Fishermen","lines":[[1948,1903,1852]]},{"name":"Woman's Cart","lines":[[424,6789,6322,5903,6116,5735,5563,5903]]},{"name":"Man's Cart","lines":[[5191,5054,4905,4660,4301,4295,4554,4660]]},{"name":"The Asar Battlefield","lines":[[2095,2088,1708,1612,1577],[1791,1577],[1791,2095]]}]}]\ No newline at end of file
Packages/MoonriseKit/Sources/SkyRenderer/SkyScene.swift +23
@@ -26,6 +26,23 @@ public struct Constellation: Sendable, Decodable {
26 public var lines: [[Int]] 26 public var lines: [[Int]]
27} 27}
28 28
29/// Another culture's figures over the same stars, from Stellarium's sky cultures (CC BY-SA).
30public struct SkyCulture: Sendable, Decodable, Identifiable {
31 public struct Figure: Sendable, Decodable {
32 public var name: String
33 public var native: String?
34 /// Polylines of HR numbers.
35 public var lines: [[Int]]
36 }
37
38 public var id: String
39 public var name: String
40 public var region: String
41 public var licence: String
42 public var source: URL
43 public var constellations: [Figure]
44}
45
29public enum Catalog { 46public enum Catalog {
30 /// Yale Bright Star Catalogue, 5th ed. (VizieR V/50): HR, RA and Dec (J2000, degrees), V, B−V, 47 /// Yale Bright Star Catalogue, 5th ed. (VizieR V/50): HR, RA and Dec (J2000, degrees), V, B−V,
31 /// proper motion in RA (× cos dec) and Dec (arcsec/yr). 48 /// proper motion in RA (× cos dec) and Dec (arcsec/yr).
@@ -35,6 +52,12 @@ public enum Catalog {
35 return rows.map { Star(hr: Int($0[0]), ra: $0[1], dec: $0[2], magnitude: $0[3], colorIndex: $0[4], pmRA: $0[5], pmDec: $0[6]) } 52 return rows.map { Star(hr: Int($0[0]), ra: $0[1], dec: $0[2], magnitude: $0[3], colorIndex: $0[4], pmRA: $0[5], pmDec: $0[6]) }
36 }() 53 }()
37 54
55 /// Built by scripts/make-skycultures.py.
56 public static let skyCultures: [SkyCulture] = {
57 let url = Bundle.module.url(forResource: "skycultures", withExtension: "json")!
58 return try! JSONDecoder().decode([SkyCulture].self, from: Data(contentsOf: url))
59 }()
60
38 public static let constellations: [Constellation] = { 61 public static let constellations: [Constellation] = {
39 let url = Bundle.module.url(forResource: "constellations", withExtension: "json")! 62 let url = Bundle.module.url(forResource: "constellations", withExtension: "json")!
40 return try! JSONDecoder().decode([Constellation].self, from: Data(contentsOf: url)) 63 return try! JSONDecoder().decode([Constellation].self, from: Data(contentsOf: url))
Packages/MoonriseKit/Sources/SkyRenderer/SkyView.swift +33 −1
@@ -78,12 +78,15 @@ public struct SkyView: View {
78 public var centerAltitude: Double 78 public var centerAltitude: Double
79 public var fieldOfView: Double 79 public var fieldOfView: Double
80 public var style: SkyStyle 80 public var style: SkyStyle
81 /// Figures to draw instead of the IAU constellation lines.
82 public var culture: SkyCulture?
81 /// Called after each draw with the time the draw took. 83 /// Called after each draw with the time the draw took.
82 public var onDraw: (@Sendable (Duration) -> Void)? 84 public var onDraw: (@Sendable (Duration) -> Void)?
83 85
84 public init(scene: SkyScene, centerAzimuth: Double = 180, centerAltitude: Double = 55, fieldOfView: Double = 110, style: SkyStyle = SkyStyle(), onDraw: (@Sendable (Duration) -> Void)? = nil) { 86 public init(scene: SkyScene, centerAzimuth: Double = 180, centerAltitude: Double = 55, fieldOfView: Double = 110, style: SkyStyle = SkyStyle(), culture: SkyCulture? = nil, onDraw: (@Sendable (Duration) -> Void)? = nil) {
85 self.scene = scene 87 self.scene = scene
86 self.style = style 88 self.style = style
89 self.culture = culture
87 self.centerAzimuth = centerAzimuth 90 self.centerAzimuth = centerAzimuth
88 self.centerAltitude = centerAltitude 91 self.centerAltitude = centerAltitude
89 self.fieldOfView = fieldOfView 92 self.fieldOfView = fieldOfView
@@ -104,6 +107,10 @@ public struct SkyView: View {
104 } 107 }
105 108
106 func drawConstellations(_ context: inout GraphicsContext, _ p: Projection) { 109 func drawConstellations(_ context: inout GraphicsContext, _ p: Projection) {
110 if let culture {
111 drawCulture(culture, &context, p)
112 return
113 }
107 var path = Path() 114 var path = Path()
108 for c in Catalog.constellations { 115 for c in Catalog.constellations {
109 for line in c.lines { 116 for line in c.lines {
@@ -119,6 +126,31 @@ public struct SkyView: View {
119 context.stroke(path, with: .color(style.line), lineWidth: 0.6) 126 context.stroke(path, with: .color(style.line), lineWidth: 0.6)
120 } 127 }
121 128
129 /// A culture's figures, named at the middle of their visible stars once zoomed in
130 /// (closer for cultures with many small figures).
131 func drawCulture(_ culture: SkyCulture, _ context: inout GraphicsContext, _ p: Projection) {
132 var path = Path()
133 let labelBelow = culture.constellations.count > 60 ? 60.0 : 110.0
134 for figure in culture.constellations {
135 var points: [CGPoint] = []
136 for line in figure.lines {
137 for (a, b) in zip(line, line.dropFirst()) {
138 guard let ha = scene.starsByHR[a], let hb = scene.starsByHR[b],
139 ha.altitude > -5 || hb.altitude > -5,
140 let pa = p.point(ha), let pb = p.point(hb) else { continue }
141 path.move(to: pa)
142 path.addLine(to: pb)
143 points += [pa, pb]
144 }
145 }
146 if fieldOfView <= labelBelow, points.count > 1 {
147 let c = CGPoint(x: points.map(\.x).reduce(0, +) / Double(points.count), y: points.map(\.y).reduce(0, +) / Double(points.count))
148 context.draw(Text(figure.name).font(.caption2).foregroundStyle(style.muted), at: c)
149 }
150 }
151 context.stroke(path, with: .color(style.line), lineWidth: 0.6)
152 }
153
122 func drawStars(_ context: inout GraphicsContext, _ p: Projection) { 154 func drawStars(_ context: inout GraphicsContext, _ p: Projection) {
123 for s in scene.stars where s.position.altitude > 0 { 155 for s in scene.stars where s.position.altitude > 0 {
124 guard let pt = p.point(s.position) else { continue } 156 guard let pt = p.point(s.position) else { continue }
Packages/MoonriseKit/Tests/SkyRendererTests/SkyRenderTests.swift +15
@@ -62,3 +62,18 @@ let evening = Date(timeIntervalSince1970: 1_792_202_400) // 2026-10-17T02:00:00Z
62 let renderer = ImageRenderer(content: SkyView(scene: scene, style: .night).frame(width: 393, height: 852)) 62 let renderer = ImageRenderer(content: SkyView(scene: scene, style: .night).frame(width: 393, height: 852))
63 #expect(renderer.cgImage != nil) 63 #expect(renderer.cgImage != nil)
64} 64}
65
66@Test func skyCulturesUseCatalogueStars() {
67 let cultures = Catalog.skyCultures
68 #expect(cultures.count == 10)
69 let known = Set(Catalog.stars.map(\.hr))
70 for c in cultures {
71 #expect(!c.constellations.isEmpty, "\(c.id)")
72 #expect(c.licence.contains("BY-SA") || c.licence.contains("BY SA"), "\(c.id)")
73 for f in c.constellations {
74 #expect(f.lines.allSatisfy { $0.count > 1 && $0.allSatisfy(known.contains) }, "\(c.id) \(f.name)")
75 }
76 }
77 // The Norse Wolf's Mouth is the Hyades and includes Aldebaran (HR 1457).
78 #expect(cultures.first { $0.id == "norse" }?.constellations.contains { $0.name == "Wolf's Mouth" && $0.lines.joined().contains(1457) } == true)
79}
THIRD_PARTY.org +3
@@ -4,6 +4,9 @@
4|- 4|-
5| Astronomy Engine v2.1.19, Copyright (c) 2019-2025 Don Cross | =Packages/MoonriseKit/Sources/CAstronomy= | MIT (=LICENSE= alongside) | https://github.com/cosinekitty/astronomy | 5| Astronomy Engine v2.1.19, Copyright (c) 2019-2025 Don Cross | =Packages/MoonriseKit/Sources/CAstronomy= | MIT (=LICENSE= alongside) | https://github.com/cosinekitty/astronomy |
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 |
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| 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 |
7| ConstellationLines v1.4, Copyright (c) 2005-2026 Marc van der Sluys | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/constellations.json= | CC BY 4.0 | https://github.com/MarcvdSluys/ConstellationLines, doi:10.5281/zenodo.10397192 | 10| ConstellationLines v1.4, Copyright (c) 2005-2026 Marc van der Sluys | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/constellations.json= | CC BY 4.0 | https://github.com/MarcvdSluys/ConstellationLines, doi:10.5281/zenodo.10397192 |
8| Natural Earth 1:110m coastline v5.1.2 | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/coastline.json= | Public domain | https://www.naturalearthdata.com | 11| Natural Earth 1:110m coastline v5.1.2 | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/coastline.json= | Public domain | https://www.naturalearthdata.com |
9| Launch Library 2 (The Space Devs) | fetched at runtime by =server/= | Free API; credit shown in the app | https://thespacedevs.com/llapi | 12| Launch Library 2 (The Space Devs) | fetched at runtime by =server/= | Free API; credit shown in the app | https://thespacedevs.com/llapi |
scripts/make-skycultures.py added +117
@@ -0,0 +1,117 @@
1#!/usr/bin/env python3
2"""Builds SkyRenderer/Resources/skycultures.json from Stellarium sky cultures (CC BY-SA only).
3
4Usage: make-skycultures.py HIP_TSV
5
6HIP_TSV is VizieR I/239/hip_main with columns HIP, RAICRS, DEICRS, Vmag (Vmag < 7.2):
7https://vizier.cds.unistra.fr/viz-bin/asu-tsv?-source=I/239/hip_main&-out=HIP,RAICRS,DEICRS,Vmag&Vmag=%3C7.2&-out.max=unlimited
8
9Stellarium lines use Hipparcos numbers; the app's catalogue is the Bright Star Catalogue (HR).
10Each HIP star is matched to the BSC star within 0.05 degrees. Lines are split where a star has no match
11(fainter than the BSC's limit), and segments with fewer than two stars are dropped.
12"""
13import json
14import math
15import sys
16import urllib.request
17from pathlib import Path
18
19ROOT = Path(__file__).resolve().parent.parent
20RES = ROOT / "Packages/MoonriseKit/Sources/SkyRenderer/Resources"
21BASE = "https://raw.githubusercontent.com/Stellarium/stellarium-skycultures/master"
22
23# id, display name. All CC BY-SA in their description.md as of 2026-10-18.
24CULTURES = [
25 ("chinese", "Chinese"),
26 ("indian", "Indian (Vedic)"),
27 ("mongolian", "Mongolian"),
28 ("japanese_moon_stations", "Japanese moon stations"),
29 ("hawaiian_starlines", "Hawaiian"),
30 ("maori", "Māori"),
31 ("tongan", "Tongan"),
32 ("boorong", "Boorong (Australia)"),
33 ("sami", "Sámi"),
34 ("norse", "Norse"),
35]
36
37
38def fetch(url):
39 with urllib.request.urlopen(url, timeout=60) as r:
40 return r.read().decode("utf-8")
41
42
43def license_section(md):
44 out, on = [], False
45 for line in md.splitlines():
46 if line.startswith("#"):
47 on = "licen" in line.lower()
48 continue
49 if on and line.strip():
50 out.append(line.strip())
51 return " ".join(out)
52
53
54def main():
55 hip = {}
56 for line in open(sys.argv[1]):
57 p = line.rstrip("\n").split("\t")
58 if len(p) == 4 and p[0].strip().isdigit() and p[1].strip() and p[2].strip():
59 hip[int(p[0])] = (float(p[1]), float(p[2]), float(p[3] or 99))
60
61 bsc = json.load(open(RES / "bsc5.json"))
62 cells = {}
63 for row in bsc:
64 cells.setdefault((int(row[2])), []).append(row)
65
66 def hr_for(h):
67 if h not in hip:
68 return None
69 ra, dec, _ = hip[h]
70 best = None
71 for d in (int(dec) - 1, int(dec), int(dec) + 1):
72 for row in cells.get(d, []):
73 dra = (row[1] - ra + 180) % 360 - 180
74 sep = math.hypot(dra * math.cos(math.radians(dec)), row[2] - dec)
75 if sep < 0.05 and (best is None or sep < best[0]):
76 best = (sep, int(row[0]))
77 return best[1] if best else None
78
79 out = []
80 for cid, name in CULTURES:
81 index = json.loads(fetch(f"{BASE}/{cid}/index.json"))
82 lic = license_section(fetch(f"{BASE}/{cid}/description.md"))
83 assert "BY-SA" in lic.replace("-BY SA", "BY-SA").replace("BY SA", "BY-SA"), (cid, lic)
84 constellations = []
85 for c in index.get("constellations", []):
86 segments = []
87 for line in c.get("lines", []):
88 current = []
89 for h in line:
90 hr = hr_for(h) if isinstance(h, int) else None
91 if hr is None:
92 if len(current) > 1:
93 segments.append(current)
94 current = []
95 else:
96 current.append(hr)
97 if len(current) > 1:
98 segments.append(current)
99 if not segments:
100 continue
101 cn = c.get("common_name", {})
102 entry = {"name": cn.get("english") or cn.get("native") or c["id"], "lines": segments}
103 if cn.get("native") and cn.get("native") != entry["name"]:
104 entry["native"] = cn["native"]
105 constellations.append(entry)
106 out.append({
107 "id": cid, "name": name, "region": index.get("region", ""),
108 "licence": lic, "source": f"https://github.com/Stellarium/stellarium-skycultures/tree/master/{cid}",
109 "constellations": constellations,
110 })
111 print(f"{cid}: {len(constellations)} of {len(index.get('constellations', []))} figures", file=sys.stderr)
112
113 (RES / "skycultures.json").write_text(json.dumps(out, ensure_ascii=False, separators=(",", ":")))
114
115
116if __name__ == "__main__":
117 main()