"""船だけ、氷なし。9月の船の存在（GFW AIS presence）を 2012→2025 で並べる。"""
import os, sys, numpy as np, tifffile
os.environ['PURE4_NO_MAIN'] = '1'
sys.path.insert(0, '/home/claude/loop/human/v3card'); sys.path.insert(0, '/home/claude/loop/lib'); sys.path.insert(0, '/home/claude/dv/cards')
import pure4 as P, render3 as r, g1, pure6 as S
from PIL import Image
OUT = '/home/claude/loop/human/ships_years'; os.makedirs(f'{OUT}/frames', exist_ok=True); os.makedirs(f'{OUT}/stills', exist_ok=True)
sc = P.sc; size = (r.W, r.H)
YEARS = list(range(2012, 2026))
# 下地: 氷を描かない（氷の色＝紙の色にして塗り潰しを無効化）。陸・緯度経度線・地名はそのまま
NOICE = dict(S.BASE, subject_mode='mass', ice_L=S.BASE['field_L'], ice_hue=S.BASE['field_hue'], ice_C=S.BASE['field_C'], smooth_km=0)
out = r.render(NOICE, f'{OUT}/base'); base = Image.open(f'{out}/render.png')
base = P.comp_rel(base, P.aa_mask(size, P.grat(sc)), 0.22, 0.16)
base = P.comp_rel(base, P.aa_mask(size, P.circle66(sc)), 0.22, 0.16)
base = P.comp_rel(base, P.aa_mask(size, P.lat_labels(sc)), 0.30, 0.20)
place_a = [P.aa_mask(size, P.text_fn(sc, it, 20)) for it in P.PLACES]
town_a = [P.aa_mask(size, P.text_fn(sc, it, 14, 0.10, dot=2.2)) for it in P.TOWNS]
def load(p):
    t = tifffile.TiffFile(p); pg = t.pages[0]; a = pg.asarray().astype(np.float64); a[a >= 999999] = 0
    tp = pg.tags['ModelTiepointTag'].value; lon0, lat0 = tp[3], tp[4]
    full = np.zeros((301, 3601)); r0 = int(round((90.05 - lat0) / 0.1)); c0 = int(round((lon0 + 180.05) / 0.1))
    full[r0:r0 + a.shape[0], c0:c0 + a.shape[1]] = a; return full
def year_field(y):
    S.pres = load(f'/home/claude/loop/human/gfw/years/{y}/layer-activity-data-0/public-global-presence-v4.0.tif'); return S.presence_field(sc)
fields = {y: year_field(y) for y in YEARS}
ref = 60.0   # 月あたりの存在時間の参照値（対数の上限）。2025年9月と同じ尺度
def to_alpha(f): return np.clip(np.log1p(f) / np.log1p(ref), 0, 1)
ORANGE = np.array(r.col(66, 58, 62), np.float32)
def comp_color(im, alpha, color):
    b = np.array(im).astype(np.float32); a = alpha[..., None]
    return Image.fromarray(np.clip(b*(1-a) + color*a, 0, 255).astype(np.uint8))
def year_mask(y):
    f = g1.font('Regular', 30*P.SS)
    def fn(d, s): d.text((r.W*s - 72*s, 72*s), f'{y}.09', font=f, fill=255, anchor='rt')
    return P.aa_mask(size, fn)
def frame(alpha, ymask):
    im = comp_color(base, np.clip(alpha, 0, 1) * 0.95, ORANGE)
    for a in place_a: im = P.comp_label(im, a)
    for a in town_a: im = P.comp_label(im, a)
    return P.comp_label(im, ymask)
alphas = {y: to_alpha(fields[y]) for y in YEARS}
# 静止画（年ごと）＋ 総量
stats = {}
for y in YEARS:
    im = frame(alphas[y], year_mask(y)); im.save(f'{OUT}/stills/{y}.png')
    f = fields[y]; stats[y] = dict(hours=float(f.sum()), cells=int((f > 0).sum()), cells_ge24h=int((f >= 24).sum()))
    print(y, stats[y], flush=True)
import json; json.dump(stats, open(f'{OUT}/stats.json', 'w'), indent=1)
# 動画: 各年 0.9 秒保持 + 0.3 秒クロスフェード（30fps）。最後の年は 3 秒保持
HOLD, FADE, FPS = 27, 9, 30
k = 0
for i, y in enumerate(YEARS):
    ym = year_mask(y); im = frame(alphas[y], ym)
    for _ in range(HOLD if y != YEARS[-1] else 90): im.save(f'{OUT}/frames/{k:04d}.png'); k += 1
    if i + 1 < len(YEARS):
        y2 = YEARS[i+1]; ym2 = year_mask(y2)
        for s in range(1, FADE + 1):
            t = s / (FADE + 1)
            a = alphas[y] * (1 - t) + alphas[y2] * t
            im2 = comp_color(base, np.clip(a, 0, 1) * 0.95, ORANGE)
            for m in place_a: im2 = P.comp_label(im2, m)
            for m in town_a: im2 = P.comp_label(im2, m)
            im2 = P.comp_label(im2, ym if t < 0.5 else ym2)
            im2.save(f'{OUT}/frames/{k:04d}.png'); k += 1
    print(y, end=' ', flush=True)
print('frames', k)
