Значение температуры OpenWeatherMap от цвета пикселя в js - PullRequest
0 голосов
/ 30 апреля 2020

Я использую openweather , чтобы получать плитки карты температуры (Weather Map V1).

Мне нужно преобразовать цвет пикселя температуры в значения температуры.

Я нашел легенду для карты здесь

После того, как все плитки нарисованы на холсте, я получаю цвета пикселей с помощью getImageData.

Проблема : некоторые цвета, которые я получаю, почему-то неправильны, потому что они не вписываются ни в один шаг в линейном градиенте (заданном легендой).

Например: rgb ( 34, 198, 232) не подходит нигде в данном диапазоне. Как мне с этим справиться?

Вот фрагмент, чтобы увидеть мою реализацию, после загрузки карты вы можете щелкнуть по ней и увидеть вывод в консоли.

Дополнительно я загрузил код на jsFiddle

function relief_tile(z, x, y) {
    return $('<img width="256" height="256">')
      .attr('crossOrigin', '')
      .attr('src', `https://cartodb-basemaps-b.global.ssl.fastly.net/light_all/${z}/${x}/${y}.png`)
  }

  function weather_tile(z, x, y) {
    return $('<img width="256" height="256">')
      .attr('crossOrigin', '')
      .attr('src', `https://tile.openweathermap.org/map/temp_new/${z}/${x}/${y}.png?appid=01c2dab301eeeff7e02ebf1748f65faa`);
  }

  function rgbToHex(rgb) {
    let r = rgb[0], g = rgb[1], b = rgb[2];
    if (r > 255 || g > 255 || b > 255)
      throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
  }

  function initSquareCanvas(canvas, size) {
    canvas.height = size;
    canvas.width = size;
  }

  let zoomLevel = 3;

  $(async function () {
    let relief_canvas = $('#relief-map')[0];
    let relief_ctx;
    if (relief_canvas) {
      initSquareCanvas(relief_canvas, Math.pow(2, zoomLevel) * 256)
      relief_ctx = relief_canvas.getContext('2d');
      relief_ctx.globalAlpha = 1;
    }

    let weather_canvas = $('#weather-map')[0];
    initSquareCanvas(weather_canvas, Math.pow(2, zoomLevel) * 256)
    let weather_ctx = weather_canvas.getContext('2d');


    function loadMap() {
      for (let y = 0; y < Math.pow(2, zoomLevel); y++) {
        for (let x = 0; x < Math.pow(2, zoomLevel); x++) {
          relief_tile(zoomLevel, x, y).on('load', function () {
            relief_ctx.drawImage(this, x * 256, y * 256, 256, 256);
            weather_tile(zoomLevel, x, y).on('load', function () {
              weather_ctx.drawImage(this, x * 256, y * 256, 256, 256);
            });
          });
        }
      }
    }

    loadMap();

    let values_rgb_gradient = [
      [-40, [130, 22, 146, 1]],
      [-30, [130, 87, 219, 1]],
      [-20, [32, 140, 236, 1]],
      [-10, [32, 196, 232, 1]],
      [0, [35, 221, 221, 1]],
      [10, [194, 255, 40, 1]],
      [20, [255, 240, 40, 1]],
      [25, [255, 194, 40, 1]],
      [30, [252, 128, 20, 1]],
    ].map(function ([_value, _color]) {
      return [_value, _color.slice(0, -1)];
    });


    function findValFromRGBColor(color, valuesGradient) {
      color = Array.from(color);
      for (let index = 0; index < valuesGradient.length; index++) {
        let [grad_value, grad_color] = valuesGradient[index];
        let next = valuesGradient[index + 1];
        if (next) { // is not last
          let [next_value, next_color] = next;

          let grad_diff_color = grad_color.map(function (v, i) {
            return next_color[i] - v;
          });

          let diff_color = color.map(function (v, i) {
            return v - grad_color[i];
          });

          let is_valid_range = grad_diff_color.map(function (v, i) {
            if (v > 0 && diff_color[i] > 0) {
              return v >= diff_color[i]
            } else if (v < 0 && diff_color[i] < 0) {
              return v <= diff_color[i];
            }
            return v === diff_color[i];
          }).every(function (v) {
            return v;
          });


          if (is_valid_range) {
            let abs_grad_diff_color = grad_diff_color.map(Math.abs);
            let max_index = abs_grad_diff_color.indexOf(Math.max(...abs_grad_diff_color));
            if (grad_diff_color[max_index] === 0) {
              return grad_value;
            }
            return grad_value + diff_color[max_index] / grad_diff_color[max_index] * (next_value - grad_value);
          }
        } else {
          return grad_value
        }
      }
    }


    function findPos(obj) {
      var curleft = 0, curtop = 0;
      if (obj.offsetParent) {
        do {
          curleft += obj.offsetLeft;
          curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return {x: curleft, y: curtop};
      }
      return undefined;
    }

    $(weather_canvas).mousedown(function (e) {
      let pos = findPos(this);
      let x = e.pageX - pos.x;
      let y = e.pageY - pos.y;
      let color = weather_ctx.getImageData(x, y, 1, 1).data;

      console.log("Clicked on : x=", x, "y=", y);
      console.log("Color at this point: ", color);
      console.log("Temperature on clicked point: ", findValFromRGBColor(color.slice(0, -1), values_rgb_gradient))
    });


  })
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>
<div class="position-relative">
  <canvas id="relief-map" width="256" height="256" class="position-absolute"></canvas>
  <canvas id="weather-map" width="256" height="256" class="position-absolute"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

</body>
</html>
...