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