計算の詳細

「いまの空色」が空の色を求める計算の解説です。三角関数(sin・cos)が分かれば読めるように書きました。概要は空の豆知識をどうぞ。すべての計算は端末内で完結し、外部データは使いません。

1. 太陽はいまどの高さにあるか

知りたいのはただひとつ、太陽高度 h(地平線から太陽を見上げた角度)です。太陽の高さを決めるものを考えてみると、実は3つしかありません。

「経度は使わないの?」と思ったら鋭い。経度は時角 H の中に折り込まれています。時計の時刻(UTC)は世界共通なので、太陽が真南から何度ズレて見えるかは、あなたの立っている経度で変わります。同じ瞬間、東京では太陽が真南でも、パリはまだ夜明け前——その差がちょうど経度の差です。

この3つの角度を1つの高さにまとめるのが、球面三角法の公式です:

sin h = sin φ · sin δ + cos φ · cos δ · cos H

見た目はいかつくても、やっていることは「場所 × 季節 × 時刻 → 太陽の高さ」の変換、それだけです。

南中に当てはめると正体が見える: 太陽が真南に来る南中(だいたい正午)では H = 0 なので cos H = 1。式は
sin h = sin φ · sin δ + cos φ · cos δ
となります。右辺は加法定理で cos(φ − δ) とまとめられるので、
sin h = cos(φ − δ)
ここで sin と cos の関係 sin h = cos(90° − h) を使うと cos(90° − h) = cos(φ − δ)。中身どうしが等しいので 90° − h = φ − δ、並べ替えて
h = 90° − (緯度 − 太陽の緯度)
「夏至の大阪の南中時の太陽の高さは 90 − (34.7 − 23.4) ≒ 78°」——中学理科でやったあの計算です。上の公式は、これを南中以外のすべての時刻に使えるよう拡張したものにすぎません。
※ cos は周期関数なので、一般には cos A = cos B から A = B とは言えません。ここで言えるのは、90° − h が 0°〜180° の範囲(h は −90°〜90°)に収まっていて、この範囲では cos が単調減少=一対一だからです。なお太陽が真北を通る場所(緯度より太陽の緯度が大きい熱帯など)では φ − δ が負になるため、一般形は絶対値をつけて h = 90° − |φ − δ| になります。

ちなみにこの公式自体も、「天頂(真上)方向のベクトル」と「太陽方向のベクトル」を作って内積を成分計算すると導けます。高校数学のベクトルの範囲です。興味があれば 公式の導出 をどうぞ。

残る材料の δ と H も、難しい観測は要りません。δ は1年かけて +23.4° と −23.4° の間をゆっくり往復する波なので sin で書けます。H は1時間に15°進む時計の針そのもの。あとは、地球の軌道が楕円で公転の速さが少し揺れるぶん(最大2°ほど)を補正すれば、実際の太陽の位置と0.01°の精度で一致します。

実際に使っている式を見る

式は Astronomical Almanac(天文年鑑)に載っている簡易太陽位置式で、NOAA などでも使われる定番の近似です。基準日(2000年1月1日正午 = J2000.0)からの経過日数を d として、上から順に計算していきます。一見デタラメに見える定数は、実は「360° ÷ 周期」(1日あたりの進み)、「J2000.0 時点のスタート位置」、「軌道の形から来る量」の3種類しかありません。各式の下に内訳を書きます。

g = 357.529 + 0.98560028 d (平均近点角: 公転の進み具合)

0.98560028 = 360° ÷ 365.2596日。「1年で1周」を1日あたりに直したもので、この「1年」は近日点(太陽に最も近づく点、1月4日ごろ)から次の近日点まで。357.529 は J2000.0 時点のスタート位置——近日点通過の約2.5日前だったので、ちょうど360°の一歩手前です。

L = 280.459 + 0.98564736 d + 1.915 sin g + 0.020 sin 2g (太陽の黄経: 公転上の位置)

0.98564736 = 360° ÷ 365.2422日。こちらの「1年」は春分から春分までで、近日点がゆっくり動くぶん g の1年とわずかに違います。280.459 は J2000.0 時点のスタート位置(春分点から測った角度)。「1.915 sin g + 0.020 sin 2g」は楕円軌道の補正(中心差)で、地球が楕円軌道を一定でない速さで回るぶんのズレを直す項。1.915 は軌道の離心率(楕円のつぶれ具合)e ≒ 0.0167 の2倍を度に直した値(2 × 0.0167 ラジアン ≒ 1.91°)、0.020 はその次の次数の補正 (5/4)e² です。

ε = 23.439 − 0.00000036 d (地軸の傾き)

23.439 は2000年時点の地軸の傾き。0.00000036°/日は、傾きが100年あたり約0.013°ずつ小さくなっている分です。

δ = asin( sin ε · sin L ) (赤緯)
α = atan2( cos ε · sin L, cos L ) (赤経: 天球上の東西方向の位置)

ここに新しい定数はありません。公転上の位置 L と地軸の傾き ε を掛け合わせて、太陽の「緯度方向の位置(赤緯)」と「東西方向の位置(赤経)」に変換しているだけです。

GMST = 280.46061837 + 360.98564736629 d (地球の自転角)

360.98564736629 = 360° + 360° ÷ 365.2422。1日に「自転1回転」+「公転で太陽の見かけがずれる約1°」で、恒星を基準にした自転角です。280.46061837 は J2000.0 時点でグリニッジが向いていた方向。

H = GMST + 経度 − α (時角)

定数なし。地球の自転角にあなたの経度を足して「あなたの真南の方向」を出し、そこから太陽の位置(赤経 α)を引いたものが時角です。

単位はすべて度(°)で、式中の三角関数に入れる角度も度数法です。角度は必要に応じて 0°〜360° に折り返して使います。なお d は厳密には「地球時(TT)」で測るものですが、ふだんの時刻(UTC)とのズレは現在1分強で、空の色への影響は無視できるため区別していません。

2. 大気のレンズ効果(大気差)

プールの底が浅く見えるのと同じで、宇宙から大気に入ってきた光は屈折して曲がります。太陽が低いほど光は大気の中を長く通るので、曲がりも大きくなります。この「浮き上がり」R(単位は分角=1/60度)を経験式(Bennett の式)で計算し、幾何学的な高度に足します:

R = 1 / tan( h + 7.31 / (h + 4.4) )
h見かけ = h + R / 60

地平線ぎりぎり(h = 0°)では R ≒ 34分 ≒ 0.57°。太陽の見かけの大きさ(約0.5°)より大きいので、「日の出」で見え始めた太陽は、幾何学的にはまだ全体が地平線の下にあります。この式のおかげでアプリの日の出・日の入りが実際の見た目と合います。

※ Bennett の式は本来「見かけの高度」から屈折量を求めるものです。本アプリでは計算で得た幾何学的高度にそのまま当てはめていますが、その誤差は最大でも1分角ほどで、空の色には影響しません。

※ 新聞や気象庁の「日の出時刻」は、太陽の上辺が地平線に接する瞬間(太陽の視半径約0.27°のぶん早い。中心の幾何学的高度 ≒ −0.8°)と定義されています。本アプリのフェーズ境界は「太陽の中心が見かけの高度0°を横切る瞬間」なので、公式の日の出・日の入り時刻とは1〜2分ずれることがあります。

3. 明るさ(lux)に変換する

太陽が高いとき(h ≥ 3°)の地上の明るさは、シンプルな式で近似できます:

E = 133800 × sin(h)1.15 [lux]

式中の 133800 という数字は、大気の外で太陽光を真っ正面から受けたときの明るさ(約13.4万 lux。太陽定数の照度版にあたる値)です。そこに sin h が掛かるのは、太陽が斜めだと同じ光が広い面積に薄く広がるから(板を斜めにすると影が伸びるのと同じ理屈)。まっすぐ 1乗 ではなく 1.15乗 なのは、低い太陽ほど分厚い大気を斜めに突き抜けるため、空気に吸収・散乱される光が増えるからです(媒質を通る光が指数的に弱まる「ランベルト・ベールの法則」の効果をひとまとめにした経験的な近似です)。

太陽が沈んだあとの薄明(−18° < h < 3°)は、きれいな式では書けないので、実測に基づく点をとって直線でつなぎます。ただし明るさそのものではなく桁(log₁₀E)をつなぐのがコツです:

太陽高度log₁₀E明るさ
h = 3°3.65約 4,500 lux
2.60約 400 lux
−6°0.53約 3.4 lux(市民薄明の終わり)
−12°−2.10約 0.008 lux(航海薄明の終わり)
−18°−3.22約 0.0006 lux(星明かりの夜空)

真昼から星空まで、明るさは1億倍以上(8桁)も変わります。明るさの数値そのものを直線でつなぐと、暗い時間帯(薄明)の変化がほぼゼロに潰れてしまうので、桁数、つまり対数(log₁₀E)で扱うのが自然です。
実は、人間の目の感覚もこの対数に比例するようにできています(ウェーバー・フェヒナーの法則)。10倍明るくなってようやく「一段明るくなった」と感じる仕組みだからこそ、月明かりの夜から真夏の直射日光まで、目が壊れずに適応できるのです。画面の明度も、この対数を0〜1に換算した値を使っています。

4. 高度を色に翻訳する

ここは物理ではなく、写真や実際の空を参考にした「デザインの辞書」です。太陽高度ごとに画面の上端と下端の色を決めた表を持っています:

太陽高度フェーズ上端下端
−18°
−12°天文薄明
−8°航海薄明
−4°市民薄明
日の出/日の入り
ゴールデンアワー
10°朝夕
30°
60°真昼

表にない高度(たとえば−2°)は、はさむ2行(−4°と0°)の色を距離の比率で混ぜます。−4°と0°のちょうど中間なら50:50という単純な混色(線形補間)です。厳密にはガンマ補正を挟むとより自然なグラデーションになりますが、ここでは計算のシンプルさと速度を優先してRGBの数値をそのまま直線的に混ぜています。これでも十分に美しく、連続的に色が変わります。

※ RGB の数値をそのまま直線的に混ぜているため、厳密にはガンマ補正を挟んで混色する方が物理的には正確です。ただ空のグラデーションでは見た目の差がほとんど出ないため、シンプルさを優先しています。

最後に、この色にステップ3の明度 B(0〜1)を掛けます。夜に真っ黒にならないよう、ほんのわずかな環境光を足して完成です:

表示色 = 環境光(R5, G7, B18) + 基準色 × B × 255

上の色見本は基準色そのもの(B = 1 相当)。実際の画面では明度が掛かるため、薄明や夜はこれより暗く表示されます。フェーズ名(「市民薄明」など)は、現在の高度に近い側の行から決まります。

5. 「今日のゴールデンアワーは何時?」の求め方

方程式を逆に解いたり二分探索を使ったりするのではなく、力ずくで調べています。その日の0:00から24:00まで1分刻みに高度を計算し(1440回)、フェーズの境界をまたいだ時刻を記録するだけ。単純ですが、北極圏の白夜のように「そもそも太陽が沈まない(境界が存在しない)」といった例外で計算が壊れるのを防ぐことができます。現代のスマホなら1440回のループ計算は一瞬で終わるため、バグが入り込む余地のない「全探索」のほうがプロダクトとして安定します。

6. 時差の扱い

太陽の計算は世界共通の時刻(UTC)で行い、画面の表示だけを各都市のタイムゾーン(Asia/Tokyo など)に変換しています。夏時間も自動で反映されます。「パリの22時」を指定すると、内部ではまずUTCの瞬間に直してから太陽の位置を計算する、という順序です。

このモデルが考慮していないもの

雲・大気中のちり(黄砂や煙霧)・光害・標高・地形による遮蔽・月明かりは考慮していません。表示されるのは「快晴・海面高度・遮るもののない地平線」を仮定した理論値です。だからこそ、通信なしで世界中のどの場所・どの時刻の空も描くことができます。