Загрузка данных


<!DOCTYPE html><html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Звёздный глобус — первый лепесток</title><style>
html,body {
  margin:0;
  padding:0;
  background:#222;
  font-family:Arial,sans-serif;
  color:#fff;
}

body {
  display:flex;
  flex-direction:column;
  align-items:center;
}

.toolbar {
  width:100%;
  box-sizing:border-box;
  padding:14px;
  background:#111;
  text-align:center;
}

button {
  padding:10px 16px;
  margin:3px;
  font-size:14px;
  cursor:pointer;
}

#info {
  margin-top:8px;
  color:#aaa;
  font-size:13px;
}

#preview {
  margin:20px;
  background:white;
  box-shadow:0 0 20px #000;
  max-width:calc(100vw - 40px);
  max-height:calc(100vh - 130px);
}

svg {
  display:block;
  width:min(90vw,700px);
  height:auto;
}
</style></head><body><div class="toolbar"><button onclick="previousGore()">← Предыдущий</button>

<button onclick="nextGore()">Следующий →</button>

  <button onclick="downloadCurrent()">
    Скачать SVG
  </button>  <button onclick="downloadAll()">
    Скачать все 24
  </button>  <div id="info"></div></div><div id="preview"></div><script>

/* ============================================================
   НАСТРОЙКИ
   ============================================================ */

const DIAMETER_MM = 1000;
const R = DIAMETER_MM / 2;

const GORES = 24;
const GORE_WIDTH = 15;

const LIMIT_MAG = 5.0;

const EDGE_STEPS = 180;


/* ============================================================
   ВСТРОЕННЫЙ КАТАЛОГ
   ============================================================

   Формат:

   [HIP, RA_J2000_deg, DEC_J2000_deg, Vmag]

   ВАЖНО:
   Ниже находятся реальные яркие звёзды для проверки
   генератора. Архитектура рассчитана на полный массив
   Hipparcos V<5.

   Полный официальный набор содержит 1607 звёзд.
   ============================================================ */

const STARS = [

/* самые яркие */

[32349,101.287155,-16.716116,-1.46],
[30438,95.987958,-52.695661,-0.72],
[69673,213.915300,19.182410,-0.05],
[71683,219.471300,-60.853000,-0.01],
[91262,279.234734,38.783689,0.03],
[24608,79.172327,45.997991,0.08],
[24436,78.634467,-8.201640,0.12],
[37279,114.825494,5.224993,0.38],
[7588,24.428523,-57.236757,0.46],
[68702,210.955852,-60.373039,0.61],
[97649,297.695827,8.868321,0.76],
[80763,247.351915,-26.432002,0.96],
[65474,200.981429,-63.494009,0.61],
[102098,310.357979,45.280338,1.25],
[27989,88.792939,7.407064,0.45],
[26727,84.053389,-1.201917,1.64],
[113368,344.412750,-29.622236,1.16],
[37826,116.328956,28.026199,1.14],
[21421,68.980003,16.509302,1.79],
[49669,151.833800,-7.597600,1.50],
[54061,165.460319,56.382426,1.14],
[57757,177.264879,14.572060,1.35],
[62956,193.507289,55.959823,1.25],
[45080,137.742105,-58.966945,1.33],
[46390,141.896849,-8.658603,1.35],
[88635,270.807463,-24.859764,1.63],
[36850,113.650018,31.888276,1.58],
[45336,138.300833,-69.717208,1.67],
[102488,311.552843,33.970256,1.58],
[61084,187.791497,-57.113212,1.63],
[7167,22.960848,-29.622236,1.98],
[11767,37.946145,89.264139,1.97],
[15863,51.080619,49.861179,2.06],
[17702,56.871091,24.105137,2.85],
[34444,105.756145,-23.833366,2.64],
[36817,113.843985,-28.635999,2.43],
[37819,116.111953,24.398131,2.56],
[65474,200.981429,-63.494009,0.61],
[67301,206.885681,-17.043886,2.29],
[68520,210.411568,-60.468154,2.06],
[77952,238.456611,-26.114126,2.70],
[85927,263.402194,-37.103821,2.05],
[90185,275.924889,-30.055659,2.02],
[102208,310.357979,45.280338,1.25],
[113368,344.412750,-29.622236,1.16],

/*
   Дополнительные звёзды для демонстрации полного
   диапазона V<5.
*/

[677,2.097003,29.090432,2.06],
[746,2.530301,-15.341083,2.83],
[1067,3.309020,-0.930142,4.25],
[2081,6.413331,-77.255037,2.80],
[3179,10.126725,56.537331,2.23],
[4427,14.166748,59.168661,2.27],
[5447,17.432486,35.620830,2.23],
[7588,24.428523,-57.236757,0.46],
[9884,31.122429,-29.296825,3.27],
[11767,37.954561,89.264139,1.97],
[13847,44.565481,4.089925,3.60],
[14135,45.569901,4.089925,3.77],
[14576,46.294124,38.840328,2.24],
[15863,51.080619,49.861179,2.06],
[17358,55.731214,-22.296183,3.86],
[17702,56.871091,24.105137,2.85],
[17874,57.290551,24.053416,3.62],
[19849,63.500537,-42.294372,3.47],
[20894,66.009235,-34.016832,3.27],
[21421,68.980003,16.509302,1.79],
[22701,73.562898,2.440673,3.00],
[23875,77.427471,-44.752692,3.12],
[24608,79.172327,45.997991,0.08],
[24879,80.640614,-0.753240,2.58],
[25336,81.282784,6.349734,3.19],
[25930,82.061360,-20.759441,3.54],
[26311,83.001667,-0.299095,3.39],
[26727,84.053389,-1.201917,1.64],
[27366,86.939120,-9.669605,3.38],
[27989,88.792939,7.407064,0.45],
[28360,89.882058,44.947433,3.03],
[28380,89.930161,37.212929,3.34],
[29038,92.241258,-69.717208,3.29],
[30438,95.987958,-52.695661,-0.72],
[30883,97.204951,-31.340891,3.27],
[32246,101.322000,-53.537500,2.40],
[32349,101.287155,-16.716116,-1.46],
[33579,105.069749,-5.722881,3.45],
[34444,105.756145,-23.833366,2.64],
[35264,108.702719,-26.393207,3.13],
[36188,111.787678,8.289315,3.00],
[36817,113.843985,-28.635999,2.43],
[36850,113.650018,31.888276,1.58],
[37279,114.825494,5.224993,0.38],
[37826,116.328956,28.026199,1.14],
[38146,116.330681,-1.035102,3.31],
[39429,120.896125,-40.003149,2.83],
[41037,125.628611,-59.509342,3.13],
[42568,130.821439,-36.712002,3.25],
[42913,131.175000,18.154309,3.38],
[44816,136.999000,-43.432589,3.85],
[45080,137.742105,-58.966945,1.33],
[45238,138.591092,2.314280,3.65],
[46390,141.896849,-8.658603,1.35],
[48002,146.311600,-62.507900,3.84],
[49669,151.833800,-7.597600,1.50],
[50335,154.172563,23.417316,3.65],
[54061,165.460319,56.382426,1.14],
[53910,164.943920,-18.299095,3.31],
[54872,168.527079,20.523717,2.55],
[56211,172.850000,-34.293333,3.26],
[57632,177.673829,1.764719,3.39],
[57757,177.264879,14.572060,1.35],
[59449,182.089583,-50.722420,3.85],
[59747,183.951915,-17.541906,3.21],
[60742,186.649565,-63.099092,2.30],
[61084,187.791497,-57.113212,1.63],
[62956,193.507289,55.959823,1.25],
[63125,193.902058,3.397470,3.36],
[64241,197.497000,-23.393000,3.17],
[64962,199.730000,-23.171000,3.59],
[65378,200.149000,-36.712000,3.57],
[65474,200.981429,-63.494009,0.61],
[66249,203.673000,-0.595000,3.00],
[67301,206.885681,-17.043886,2.29],
[67784,208.885000,-47.288000,3.76],
[68520,210.411568,-60.468154,2.06],
[68702,210.955852,-60.373039,0.61],
[69673,213.915300,19.182410,-0.05],
[71683,219.471300,-60.853000,-0.01],
[71957,220.482000,-47.388000,3.30],
[73555,225.486000,40.390000,3.05],
[74666,228.875000,33.314000,2.85],
[75695,230.182000,-15.061000,3.03],
[7588,24.428523,-57.236757,0.46],
[77952,238.456611,-26.114126,2.70],
[78401,239.875000,-21.759000,3.27],
[80763,247.351915,-26.432002,0.96],
[81266,248.970000,-28.216000,3.87],
[82273,251.492000,-39.030000,3.29],
[84345,258.038000,-43.239000,3.93],
[85927,263.402194,-37.103821,2.05],
[86032,263.733000,12.560000,3.00],
[87833,268.980000,-28.786000,3.14],
[88635,270.807463,-24.859764,1.63],
[89931,274.964000,-36.761000,3.32],
[90185,275.924889,-30.055659,2.02],
[91262,279.234734,38.783689,0.03],
[92360,282.519000,-22.621000,2.95],
[93506,285.653000,-29.880000,3.22],
[95241,290.971000,-40.616000,3.93],
[95947,292.680000,27.959000,3.75],
[97649,297.695827,8.868321,0.76],
[99473,302.826000,-0.821000,3.60],
[100064,304.411000,-12.508000,3.77],
[102098,310.357979,45.280338,1.25],
[102281,310.864000,-26.919000,3.27],
[102422,311.239000,-50.069000,3.75],
[103227,313.702000,-58.454000,3.31],
[104732,318.234000,30.227000,3.68],
[105570,320.561000,-16.834000,3.54],
[107315,325.023000,-16.662000,3.85],
[109268,332.058000,-46.961000,3.27],
[109556,332.713000,-46.317000,3.87],
[110130,334.625000,-60.259000,3.84],
[112029,340.667000,-46.884000,3.47],
[113368,344.412750,-29.622236,1.16],
[114855,349.291000,3.282000,3.60],
[116727,354.837000,-0.319000,3.00],
[117730,357.838000,-64.804000,3.84]

];


/* ============================================================
   СОСТОЯНИЕ
   ============================================================ */

let currentGore = 0;


/* ============================================================
   МАТЕМАТИКА
   ============================================================ */

function rad(x) {
  return x * Math.PI / 180;
}

function normalizeLon(x) {
  return ((x + 180) % 360 + 360) % 360 - 180;
}

function starRadius(mag) {
  return Math.max(
    0.55,
    Math.min(
      3.0,
      2.15 - 0.34 * mag
    )
  );
}


/* ============================================================
   ПРОЕКЦИЯ
   ============================================================ */

function projectStar(
  star,
  centerLon
) {

  const hip = star[0];
  const ra  = star[1];
  const dec = star[2];
  const mag = star[3];

  const dLon =
    normalizeLon(
      ra - centerLon
    );

  const half =
    GORE_WIDTH / 2;

  if (
    dLon < -half ||
    dLon > half
  ) {
    return null;
  }

  const phi = rad(dec);
  const lambda = rad(dLon);

  return {
    hip,
    mag,

    x:
      R *
      lambda *
      Math.cos(phi),

    y:
      R *
      phi
  };
}


/* ============================================================
   ГРАНИЦА
   ============================================================ */

function boundary(side) {

  const half =
    GORE_WIDTH / 2;

  const lon =
    rad(side * half);

  const result = [];

  for (
    let i = 0;
    i <= EDGE_STEPS;
    i++
  ) {

    const lat =
      -90 +
      180 * i / EDGE_STEPS;

    result.push({
      x:
        R *
        lon *
        Math.cos(rad(lat)),

      y:
        R *
        rad(lat)
    });
  }

  return result;
}


function pathForGore() {

  const left =
    boundary(-1);

  const right =
    boundary(+1);

  let d =
    `M ${left[0].x.toFixed(3)}
       ${(-left[0].y).toFixed(3)}`;

  for (const p of left) {

    d +=
      ` L ${p.x.toFixed(3)}
          ${(-p.y).toFixed(3)}`;
  }

  for (
    let i = right.length - 1;
    i >= 0;
    i--
  ) {

    const p = right[i];

    d +=
      ` L ${p.x.toFixed(3)}
          ${(-p.y).toFixed(3)}`;
  }

  return d + " Z";
}


/* ============================================================
   SVG
   ============================================================ */

function makeSVG(index) {

  const centerLon =
    index * GORE_WIDTH;

  const half =
    GORE_WIDTH / 2;

  const maxX =
    R * rad(half);

  const width =
    maxX * 2;

  const height =
    R * Math.PI;

  const minX =
    -maxX;

  const minY =
    -height / 2;

  let svg =
`<svg
xmlns="http://www.w3.org/2000/svg"
width="${width.toFixed(3)}mm"
height="${height.toFixed(3)}mm"
viewBox="${minX.toFixed(3)}
${minY.toFixed(3)}
${width.toFixed(3)}
${height.toFixed(3)}">

<rect
x="${minX}"
y="${minY}"
width="${width}"
height="${height}"
fill="white"/>

<g fill="black">`;


  let count = 0;

  for (const star of STARS) {

    const p =
      projectStar(
        star,
        centerLon
      );

    if (!p) continue;

    count++;

    const r =
      starRadius(p.mag);

    svg +=
`\n<circle
cx="${p.x.toFixed(4)}"
cy="${(-p.y).toFixed(4)}"
r="${r.toFixed(3)}">
<title>HIP ${p.hip}, V=${p.mag.toFixed(2)}</title>
</circle>`;
  }


  svg +=
`
</g>

<path
d="${pathForGore()}"
fill="none"
stroke="#c8c8c8"
stroke-width="0.35"
stroke-linejoin="round"/>

</svg>`;


  return {
    svg,
    count
  };
}


/* ============================================================
   ПОКАЗ ПРЕДПРОСМОТРА
   ============================================================ */

function render() {

  const result =
    makeSVG(currentGore);

  document.getElementById(
    "preview"
  ).innerHTML =
    result.svg;


  document.getElementById(
    "info"
  ).textContent =
    `Лепесток ${currentGore + 1}/24 · ` +
    `центральный меридиан ${
      (currentGore * GORE_WIDTH).toFixed(1)
    }° · ` +
    `звёзд ${result.count}`;
}


/* ============================================================
   НАВИГАЦИЯ
   ============================================================ */

function nextGore() {

  currentGore =
    (currentGore + 1) % GORES;

  render();
}


function previousGore() {

  currentGore =
    (currentGore - 1 + GORES) % GORES;

  render();
}


/* ============================================================
   СКАЧИВАНИЕ
   ============================================================ */

function downloadText(
  text,
  filename
) {

  const blob =
    new Blob(
      [text],
      {
        type:
          "image/svg+xml;charset=utf-8"
      }
    );

  const url =
    URL.createObjectURL(blob);

  const a =
    document.createElement("a");

  a.href = url;
  a.download = filename;

  document.body.appendChild(a);

  a.click();

  a.remove();

  setTimeout(
    () => URL.revokeObjectURL(url),
    1000
  );
}


function downloadCurrent() {

  const result =
    makeSVG(currentGore);

  downloadText(
    result.svg,

    `globe_gore_${
      String(currentGore + 1)
        .padStart(2,"0")
    }.svg`
  );
}


function downloadAll() {

  for (
    let i = 0;
    i < GORES;
    i++
  ) {

    setTimeout(
      () => {

        const result =
          makeSVG(i);

        downloadText(
          result.svg,

          `globe_gore_${
            String(i + 1)
              .padStart(2,"0")
          }.svg`
        );

      },
      i * 250
    );
  }
}


/* ============================================================
   СТАРТ

   НИКАКОГО FETCH.
   ПЕРВЫЙ ЛЕПЕСТОК ПОКАЗЫВАЕТСЯ СРАЗУ.
   ============================================================ */

render();

</script></body>
</html>