2018年5月29日火曜日

Leaflet 1.3 - 4-8 Non-geographical maps

4-8 Non-geographical maps
A primer on L.CRS.Simple, how to make maps with no concept of “latitude” or “longitude”.

L.CRS.Simple 入門で、緯度と経度の概念のないマップを作成する方法です。


Not of this earth
この地球ではない

Sometimes, maps do not represent things on the surface of the earth and, as such, do not have a concept of geographical latitude and geographical longitude. Most times this refers to big scanned images, such as game maps.

ときには、マップは地球の表面上のものを要求しませんが、そのため、地理的な緯度と経度を持ちません。ほとんどの場合、これは、ゲームマップのような、大きなスキャン画像に関連します。

For this tutorial we’ve picked a starmap from Star Control II, a game that is now available as the open-source project The Ur-Quan Masters. These maps were made with a tool to read the open-source data files of the game, and look like this:

このチュートリアルのために、オープンソースプロジェクト The Ur-Quan Masters として現在利用できるゲーム、Star Control II から starmap を取り上げています。これらのマップは、ゲームのオープンソースデータファイルを読み込むツールで作成され、このように見えます:


(訳注:画像は、「Star Control」の 「starmaps(http://www.star-control.com/starmaps.php)」の「Name」の「07」を加工し使用します。)



The game has a built-in square coordinate system, as can be seen in the corners. This will allow us to establish a coordinate system.

ゲームは、角に見られるように、組み込み正方形座標システムがあります。これは座標システムを設置することを許可します。


CRS.Simple

CRS stands for coordinate reference system, a term used by geographers to explain what the coordinates mean in a coordinate vector. For example, [15, 60] represents a point in the Indian Ocean if using latitude-longitude on the earth, or the solar system Krueger-Z in our starmap.

coordinate reference system(座標参照系)を表す CRS、用語は、コーディネート(座標)はコーディネートベクタで意味することを説明するために地理学者によって使用されます。例えば、[15, 60] は、地球の緯度経度、または、starmap で solar system Krueger-Z を使用しているなら、インド洋にあるポイントを表します。

A Leaflet map has one CRS (and one CRS only), that can be changed when creating the map. For our game map we’ll use CRS.Simple, which represents a square grid:

Leaflet マップが一つの CRS (そして CRS ひとつだけ) 持っていますが、これはマップを作成するとき変えられます。ゲームマップのために、正方形グリッドを表す、CRS.Simple を使います:
var map = L.map('map', {
 crs: L.CRS.Simple
});
Then we can just add a L.ImageOverlay with the starmap image and its approximate bounds:

次に、starmap 画像とそのおおよその境界(bounds)で L.ImageOverlay をすぐ(下)に追加します。
var bounds = [[0,0], [1000,1000]];
var image = L.imageOverlay('uqm_map_full.png', bounds).addTo(map);
And show the whole map:

そして、マップ全体を表示します:
map.fitBounds(bounds);

This example doesn’t quite work, as we cannot see the whole map after doing a fitBounds().

この例はうまく動作しないので、fitBounds() を実行した後にマップ全体を見られません。


Common gotchas in CRS.Simple maps
CRS.Simple マップの一般的な了解事項

In the default Leaflet CRS, CRS.Earth, 360 degrees of longitude are mapped to 256 horizontal pixels (at zoom level 0) and approximately 170 degrees of latitude are mapped to 256 vertical pixels (at zoom level 0).

デフォルト Leaflet CRS、CRS.Earth、360度の経度は、(ズームレベル0で)256水平ピクセルにマップし、そして、おおよそ170度の緯度は、(ズームレベル0で)256垂直ピクセルにマップします。

In a CRS.Simple, one horizontal map unit is mapped to one horizontal pixel, and idem with vertical. This means that the whole map is about 1000x1000 pixels big and won’t fit in our HTML container. Luckily, we can set minZoom to values lower than zero:

CRS.Simple では、1水平マップ単位は1水平ピクセルでマップされ、垂直も同じです。これは、マップ全体はおよそ 1000x1000 ピクセルの大きさで、HTML コンテナにぴったり合いません。幸いに、minZoom を0以下に値を設定できます:
var map = L.map('map', {
 crs: L.CRS.Simple,
 minZoom: -5
});

Pixels vs. map units

One common mistake when using CRS.Simple is assuming that the map units equal image pixels. In this case, the map covers 1000x1000 units, but the image is 2315x2315 pixels big. Different cases will call for one pixel = one map unit, or 64 pixels = one map unit, or anything. Think in map units in a grid, and then add your layers (L.ImageOverlays, L.Markers and so on) accordingly.

CRS.Simple を使用するとき一つの一般的な間違いは、マップ単位が画像ピクセルと等しいと仮定することです。この場合、マップは 1000x1000 単位でカバーしますが、画像は 2315x2315 ピクセルの大きさです。別の場合は、1ピクセル=1マップユニット、または、64ピクセル=1マップ単位などです。1グリッド内でマップ単位を考え、それから、それに応じて(L.ImageOverlays、L.Markers などの)レイヤを追加します。

In fact, the image we’re using covers more than 1000 map units - there is a sizable margin. Measuring how many pixels there are between the 0 and 1000 coordinates, and extrapolating, we can have the right coordinate bounds for this image:

実際に、使用する画像は、1000 マップ単位以上をカバーし - 相当の大きさの余白があります。0 と 1000 座標の間に何ピクセルあるか測定し、そして推測し、この画像の右座標境界を持つことができます:

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

(訳注:07.png を加工した画像は、3168x3168 ピクセルで、y軸の0から1000の間のピクセル数は2942です。

y 3062[y0]-120[y1000]=2942, 2942/1000=2.942

0から画像の下端までのピクセル数とy軸値は、

y0 3168-3062=104, 104/2.942=35.4

0から画像の上端までのピクセル数とy軸値は、

y1 3062/2.942=1040.8

y軸の0から1000の間のピクセル数も2942です。

x 3064[x1000]-122[x0]=2942, 2942/1000=2.942

0から画像の左端までのピクセル数とx軸値は、

x0 122/2.942=41.5

0から画像の右端までのピクセル数とx軸値は、

x1 3168-122=3046, 3046/2.942=1035.4

var bounds = [[-35.4,-41.5], [1040.8,1035.4]];





While we’re at it, let’s add some markers:

それをいじくる間、いくつかマーカを追加しましょう。
var sol = L.latLng([ 145, 175.2 ]);
L.marker(sol).addTo(map);
map.setView( [70, 120], 1);

This is not the LatLng you’re looking for
これはあなたが探している LatLng ではありません

You’ll notice that Sol is at coordinates [145,175] instead of [175,145], and the same happens with the map center. Coordinates in CRS.Simple take the form of [y, x] instead of [x, y], in the same way Leaflet uses [lat, lng] instead of [lng, lat].

Sol は、座標 [175,145] の替わりに [145,175] にあり、同じことはマップの中心(center)で起こります。CRS.Simple で座標は、[x, y] の替わりに [y, x] の形式をとり、同じように、Leaflet は、[lng, lat] の替わりに [lat, lng] を使います。

(In technical terms, Leaflet prefers to use [northing, easting] over [easting, northing] - the first coordinate in a coordinate pair points “north” and the second points “east”)

(専門用語では、[easting(東距), northing(北距)] に優先して [northing, easting] を使う方がよく、座標対で最初の座標は「北(北緯)」を示し、2番めは、「東(東経)」を示します。)

The debate about whether [lng, lat] or [lat, lng] or [y, x] or [x, y] is not new, and there is no clear consensus. This lack of consensus is why Leaflet has a class named L.LatLng instead of the more confusion-prone L.Coordinate.

[lng, lat] か [lat, lng]、または、 [y, x] か [x, y] についての論争は、新しいものではありません、そして、はっきりしたコンセンサスはありません。このコンセンサスの欠如は、Leaflet がより混乱傾向のある L.Coordinate の替わりに L.LatLng という名前のクラスがある理由です。

If working with [y, x] coordinates with something named L.LatLng doesn’t make much sense to you, you can easily create wrappers for them:

もし、L.LatLng という名前のもので [y, x] 座標で実行することが納得できないなら、それらのラッパーを簡単に作成できます:
var yx = L.latLng;

var xy = function(x, y) {
 if (L.Util.isArray(x)) {    // When doing xy([x, y]);
  return yx(x[1], x[0]);
 }
 return yx(y, x);  // When doing xy(x, y);
};
Now we can add a few stars and even a navigation line with [x, y] coordinates:

では、2、3個の星と [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);
The map looks pretty much the same, but the code is a bit more readable:

マップは全く同じように見えますが、コードはもう少し読みやすくなっています。



コード全体
<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="./leaflet13/leaflet.css" />
  <script src="./leaflet13/leaflet.js"></script>
  <title>Non-geographical maps</title>
 </head>
 <body>
  <div id="map" style="width: 600px; height: 400px;"></div>
  <script>
   //CRS.Simple
   var map = L.map('map', {
    crs: L.CRS.Simple,
    minZoom: -5 //Common gotchas in CRS.Simple maps
   });
   // var bounds = [[0,0], [1000,1000]];
   //Pixels vs. map units
   var bounds = [[-35.4,-41.5], [1040.8,1035.4]];
   var image = L.imageOverlay('uqm_map_07.png', bounds).addTo(map);
   /*
   map.fitBounds(bounds);
   
   var sol = L.latLng([ 145, 175.2 ]);
   L.marker(sol).addTo(map);
   map.setView( [70, 120], 1);
   */
   //This is not the LatLng you’re looking for
   var yx = L.latLng;

   var xy = function(x, y) {
    if (L.Util.isArray(x)) {  // When doing xy([x, y]);
     return yx(x[1], x[0]);
    }
    return yx(y, x);  // When doing xy(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);
   map.setView( [70, 120], 1);
  </script>
 </body>
</html>

0 件のコメント: