Leaflet

オープンソースの JavaScript ライブラリ
モバイルフレンドリーなインタラクティブ地図用

← チュートリアル

地球外の地図

地図は、地球の表面にあるものを表現するとは限らず、地理的な緯度と経度の概念を持たない場合があります。ほとんどの場合、これはゲームマップのような大きなスキャン画像のことです。

このチュートリアルでは、現在オープンソースプロジェクト「The Ur-Quan Masters」として利用可能なゲーム「Star Control II」の星図を選択しました。これらの地図は、ゲームのオープンソースデータファイルを読み取るツールを使って作成されました(ウェブページは削除されたようです。アーカイブ版のこちらを参照してください)。以下のような外観をしています。


ゲームには、角に見られるように、組み込みの正方形座標系があります。これにより、座標系を確立することができます。


CRS.Simple

CRSは、座標参照系の略で、地理学者が座標ベクトルにおいて座標が何を意味するのかを説明するために使用する用語です。たとえば、[15, 60]は、地球上の緯度経度を使用する場合はインド洋の点を、この星図ではクルーガーZの太陽系を表します。

Leafletマップには1つのCRS(および1つのCRSのみ)があり、マップの作成時に変更できます。ゲームマップでは、正方形グリッドを表すCRS.Simpleを使用します。

var map = L.map('map', {
	crs: L.CRS.Simple
});

次に、星図の画像と、そのおおよその境界を持つL.ImageOverlayを追加します。

var bounds = [[0,0], [1000,1000]];
var image = L.imageOverlay('uqm_map_full.png', bounds).addTo(map);

そして、マップ全体を表示します。

map.fitBounds(bounds);
このスタンドアロンの例をご覧ください。

fitBounds()を実行した後、マップ全体が表示されないため、この例は完全には機能しません。

CRS.Simpleマップでよくある落とし穴

デフォルトのLeaflet CRSであるCRS.Earthでは、経度の360度が256水平ピクセル(ズームレベル0)にマッピングされ、緯度の約170度が256垂直ピクセル(ズームレベル0)にマッピングされます。

CRS.Simpleでは、1つの水平マップ単位が1つの水平ピクセルにマッピングされ、垂直も同様です。これは、マップ全体が約1000x1000ピクセルと大きく、HTMLコンテナに収まらないことを意味します。幸いなことに、minZoomをゼロより低い値に設定できます。

var map = L.map('map', {
	crs: L.CRS.Simple,
	minZoom: -5
});

ピクセルとマップ単位

CRS.Simpleを使用する際によくある間違いは、マップ単位が画像のピクセルと等しいと仮定することです。この場合、マップは1000x1000単位をカバーしますが、画像は2315x2315ピクセルと大きいです。1ピクセル=1マップ単位、または64ピクセル=1マップ単位など、さまざまなケースが考えられます。グリッド内のマップ単位で考え、それに応じてレイヤー(L.ImageOverlayL.Markerなど)を追加してください。

実際、使用している画像は1000マップ単位以上をカバーしています。かなりの余白があります。0〜1000の座標間のピクセル数を測定し、外挿すると、この画像の適切な座標境界が得られます。

var bounds = [[-26.5,-25], [1021.5,1023]];
var image = L.imageOverlay('uqm_map_full.png', bounds).addTo(map);

ついでに、いくつかのマーカーを追加しましょう。

var sol = L.latLng([ 145, 175.2 ]);
L.marker(sol).addTo(map);
map.setView( [70, 120], 1);
このスタンドアロンの例をご覧ください。

これは探しているLatLngではありません

ソルは[175, 145]ではなく[145, 175]の座標にあり、マップの中心でも同様のことが起こることに気づくでしょう。CRS.Simpleの座標は、Leafletが[lng, lat]の代わりに[lat, lng]を使用するのと同じように、[x, y]の代わりに[y, x]の形式を取ります。

(技術的には、Leafletは[easting, northing]よりも[northing, easting]を使用することを好みます。座標ペアの最初の座標は「北」を指し、2番目の座標は「東」を指します。)

[lng, lat]または[lat, lng]または[y, x]または[x, y]のどちらが適切かについての議論は新しいものではなく、明確な合意はありません。この合意の欠如が、Leafletがより混乱しやすいL.CoordinateではなくL.LatLngというクラスを持つ理由です。

L.LatLngという名前のもので[y, x]座標を扱うことがあまり意味をなさないと感じる場合は、それらのラッパーを簡単に作成できます。

var yx = L.latLng;

var xy = function(x, y) {
	if (Array.isArray(x)) {    // When doing xy([x, y]);
		return yx(x[1], x[0]);
	}
	return yx(y, x);  // When doing xy(x, y);
};

これで、いくつかの星を追加したり、[x, y]座標でナビゲーションラインを追加したりできます。

var sol      = xy(175.2, 145.0);
var mizar    = xy( 41.6, 130.1);
var kruegerZ = xy( 13.4,  56.5);
var deneb    = xy(218.7,   8.3);

L.marker(     sol).addTo(map).bindPopup(      'Sol');
L.marker(   mizar).addTo(map).bindPopup(    'Mizar');
L.marker(kruegerZ).addTo(map).bindPopup('Krueger-Z');
L.marker(   deneb).addTo(map).bindPopup(    'Deneb');

var travel = L.polyline([sol, deneb]).addTo(map);

マップの外観はほとんど変わりませんが、コードは少し読みやすくなります。

このスタンドアロンの例をご覧ください。