mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
feat(intro): add the First Light intro sequence
A deterministic WebGL film of God's Eye View coming online: a seed on Null Island grows into a culture that draws the planet, indexes the surface tile by tile, ingests the live feeds and outgrows its dish, ending on the logo. Renders 16:9 and 9:16 frame by frame in headless Chromium; the procedural score is locked to the same clock. Data and imagery are fetched by the scripts in tools/ and are not committed. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KqP857pippaUs6konSDfe8
This commit is contained in:
37 files changed
+15507
No files matched your search
@@ -0,0 +1,76 @@
|
||||
"""Deliverable 3: clouds.jpg (4096x2048) grayscale cloud opacity map for one recent day.
|
||||
|
||||
Source: NASA GIBS daily VIIRS Corrected Reflectance True Colour for the same UTC
|
||||
date from three satellites (NOAA-20, NOAA-21, Suomi NPP), EPSG:4326 level 3
|
||||
(10x5 tiles of 512px = 5120x2560), downsampled to 4096x2048.
|
||||
|
||||
Cloud metric per satellite:
|
||||
whiteness = min(R,G,B) (clouds are bright in every channel)
|
||||
greyness = 1 - saturation (clouds are colourless)
|
||||
c = ramp(whiteness) * smoothstep(greyness)
|
||||
The same metric computed on the Esri base map (earth_day_4k.jpg) is subtracted
|
||||
to suppress bright deserts, salt flats, snow and ice that would otherwise read
|
||||
as cloud. The three per-satellite results are combined with a per-pixel median
|
||||
(nan-aware), which removes swath-edge haze / glint streaks that only one
|
||||
satellite shows, and fills swath gaps. Remaining no-data (polar night) -> 0.
|
||||
|
||||
Usage: python3 build_clouds.py [YYYY-MM-DD]
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from common import OUT, gibs_mosaic
|
||||
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
DATE = sys.argv[1] if len(sys.argv) > 1 else "2026-10-15"
|
||||
LAYERS = [
|
||||
"VIIRS_NOAA20_CorrectedReflectance_TrueColor",
|
||||
"VIIRS_NOAA21_CorrectedReflectance_TrueColor",
|
||||
"VIIRS_SNPP_CorrectedReflectance_TrueColor",
|
||||
]
|
||||
W, H = 4096, 2048
|
||||
|
||||
|
||||
def smooth(x, a, b):
|
||||
t = np.clip((x - a) / (b - a), 0, 1)
|
||||
return t * t * (3 - 2 * t)
|
||||
|
||||
|
||||
def metric(rgb):
|
||||
rgb = rgb.astype(np.float32) / 255.0
|
||||
mn = rgb.min(2)
|
||||
mx = rgb.max(2)
|
||||
sat = (mx - mn) / (mx + 1e-4)
|
||||
white = np.clip((mn - 0.14) / 0.86, 0, 1) ** 1.15 # linear-ish: keeps texture inside thick cloud
|
||||
return white * smooth(1.0 - sat, 0.65, 0.95), mx
|
||||
|
||||
|
||||
def main():
|
||||
base = np.asarray(Image.open(os.path.join(OUT, "earth_day_4k.jpg")).convert("RGB"))
|
||||
base_c, _ = metric(base)
|
||||
# dilate the base metric a few px so small Esri/VIIRS coastline misregistration
|
||||
# does not leave bright outlines along desert and ice coasts
|
||||
from PIL import ImageFilter
|
||||
base_c = np.asarray(Image.fromarray((base_c * 255).astype(np.uint8)).filter(ImageFilter.MaxFilter(3)).filter(ImageFilter.GaussianBlur(2))).astype(np.float32) / 255
|
||||
stack = []
|
||||
for L in LAYERS:
|
||||
m = gibs_mosaic(L, DATE, "250m", 3, 5, 10, "jpg").resize((W, H), Image.LANCZOS)
|
||||
c, mx = metric(np.asarray(m))
|
||||
c = np.clip(c - base_c * 1.1, 0, 1)
|
||||
c[mx < 0.03] = np.nan # no-data (black)
|
||||
stack.append(c)
|
||||
stack = np.stack(stack)
|
||||
with np.errstate(all="ignore"):
|
||||
import warnings
|
||||
warnings.simplefilter("ignore")
|
||||
cloud = np.nanmedian(stack, axis=0)
|
||||
cloud = np.nan_to_num(cloud, nan=0.0)
|
||||
cloud = np.clip((cloud - 0.06) / 0.94, 0, 1) # black point: kill faint surface/haze residue
|
||||
img = Image.fromarray(np.clip(cloud * 255 + 0.5, 0, 255).astype(np.uint8), "L")
|
||||
img.save(os.path.join(OUT, "clouds.jpg"), quality=92, optimize=True, progressive=True)
|
||||
print("done", DATE, "mean opacity %.3f" % cloud.mean())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user