// @wuys/map · math/projections/equal-area-cylindrical.js
//
// HOBO-DYER Equal-Area Cylindrical projection.
// Standard parallel: 37.5° (the Hobo-Dyer choice — popular modern variant).
//
//   x = lon · cos(37.5°)
//   y = sin(lat) / cos(37.5°)
//
// Properties:
//   • RECTANGULAR map boundaries — flat top + bottom edges.
//   • PRESERVES AREA — Greenland, Africa, Antarctica all correctly proportioned.
//   • DOES distort shapes (countries near equator squashed shorter+wider;
//     countries near poles stretched taller). Trade-off for the flat edges.
//
// Why this projection: it's the cleanest "rectangular and area-honest" choice.
// Alternatives in the same family:
//   • Behrmann (standard parallel 30°) — slightly more equator-shape-accurate
//   • Gall–Peters (45°) — more iconic, but uglier proportions
//   • Plate Carrée (0°) — equirectangular, NOT area-preserving (poles huge)
// 37.5° is the modern compromise (Hobo-Dyer 2002).

const PHI_S = 37.5 * Math.PI / 180;
const COS_PHI_S = Math.cos(PHI_S);          // ≈ 0.7934
const X_HALF = Math.PI * COS_PHI_S;          // ≈ 2.4934 — half of x range at full world
const Y_HALF = 1 / COS_PHI_S;                // ≈ 1.2604 — half of y range at ±90° lat

const VB_W = 2000, VB_H = 1000;
const SCALE = Math.min((VB_W / 2) / X_HALF, (VB_H / 2) / Y_HALF);
const CX = VB_W / 2;
const CY = VB_H / 2;

/** Forward: (lon, lat) → [vbx, vby] in viewBox coords. */
export function project(lon, lat) {
  const lonR = lon * Math.PI / 180;
  const latR = lat * Math.PI / 180;
  const xn = lonR * COS_PHI_S;
  const yn = Math.sin(latR) / COS_PHI_S;
  return [CX + xn * SCALE, CY - yn * SCALE];
}

/** Inverse: viewBox coords → (lon, lat). */
export function unproject(vbx, vby) {
  const xn = (vbx - CX) / SCALE;
  const yn = (CY - vby) / SCALE;
  let sinLat = yn * COS_PHI_S;
  if (sinLat >  1) sinLat =  1;
  if (sinLat < -1) sinLat = -1;
  const lat = Math.asin(sinLat) * 180 / Math.PI;
  const lon = (xn / COS_PHI_S) * 180 / Math.PI;
  return { lon, lat };
}

/** Approximate viewBox units per degree of LATITUDE at the equator.
 *  Used by the labels layer to size text relative to a country's projected
 *  height. For Hobo-Dyer: dy/dlat at equator = cos(lat)/cos(PHI_S) = 1/cos(PHI_S).
 *  Multiplied by SCALE × (π/180) = SCALE × 0.01745. */
export const VBOX_UNITS_PER_DEG_LAT_EQUATOR = (1 / COS_PHI_S) * SCALE * Math.PI / 180;

export const meta = {
  id: "equalAreaCylindrical",
  name: "Equal-Area Cylindrical (Hobo-Dyer)",
  type: "cylindrical",
  equalArea: true,
  rectangular: true,
  viewBox: { w: VB_W, h: VB_H, cx: CX, cy: CY },
};
