search
LoginSignup
0
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

Mapbox Advent Calendar 2020 Day 5

posted at

updated at

表示されている地図の「1ピクセルは何メートルか?」を計算する

地図の世界の座標は緯度経度や実測のメートルやキロメートルなどがよく使用されますが、地図を使用したソフトウェアでは、しばしば「画面上のピクセル単位」で距離を指定したい場合があります。

例えば地図上の POI をクリックで選択するときの選択可能範囲は実測のメートル指定では地図を拡大するとヒットしやすく、縮小するとヒットしにくくなってしまうのでピクセル単位で指定したいです。

そのためには「1ピクセルは地図上では何メートルか?」を求める必要があります。

const spanElem = document.getElementById('label');

const map = new mapboxgl.Map({
  container: 'map',
  center: [-122.486052, 37.830348],
  zoom: 15,
  style: {
    version: 8,
    sources: {
      OSM: {
        type: "raster",
        tiles: [
          "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",
        ],
        tileSize: 256,
        attribution:
        "OpenStreetMap",
      },
    },
    layers: [{
      id: "BASEMAP",
      type: "raster",
      source: "OSM",
      minzoom: 0,
      maxzoom: 18,
    }],
  },      
});

map.addControl(new mapboxgl.NavigationControl({
  showCompass: false,
}));

map.once('load', () => {

  map.on('moveend', e => {
    const clientWidth = window.innerWidth;
    const distance = getMapDistance();
    const metersPerPixel = distance / clientWidth;
    console.log('Meters per pixel = ', metersPerPixel);
    spanElem.innerHTML = `${metersPerPixel.toFixed(2)} meters/pixel`;
  });

});

function getMapDistance() {
  const bounds = map.getBounds();
  const ne = bounds.getNorthEast();
  const sw = bounds.getSouthWest();
  const topLeft = turf.point([ne.lng, ne.lat]);
  const topRight = turf.point([sw.lng, ne.lat]);
  const bottomLeft = turf.point([ne.lng, sw.lat]);
  const bottomRight = turf.point([sw.lng, sw.lat]);

  const middleLeft = turf.midpoint(topLeft, bottomLeft);
  const middleRight = turf.midpoint(topRight, bottomRight);
  const distance = turf.distance(middleLeft, middleRight, { units: 'meters' });
  return distance;
}

「地図上の何メートル?」は、地図の縮尺はもちろん、メルカトル図法ですから位置によっても変わります(北へ行けば行くほど小さくなります)。
そのため、moveend イベントで表示地図範囲が変更される度に求める必要があります。

実際の計算は。

  • A: 画面上での地図領域の幅(pixel)
  • B: 画面上の四隅を緯度経度に変換し、その(幅の)距離(メートル)

を求めた後に、B ÷ A を行います。
B では2点の緯度経度間の距離を turf.js の distance メソッドで求めています。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0
Help us understand the problem. What are the problem?