非地球範疇
有時,地圖並不代表地球表面的事物,因此不具備地理緯度和地理經度的概念。大多數情況下,這指的是大型掃描影像,例如遊戲地圖。
在本教學中,我們選擇了來自《星際控制II》的星圖,這款遊戲現在以開源專案 The Ur-Quan Masters 的形式提供。這些地圖是使用一個工具來讀取遊戲的開源資料檔製作的(網頁似乎已關閉,請參閱封存的版本),看起來像這樣

正如在角落所見,遊戲具有內建的方形坐標系統。這將使我們能夠建立坐標系統。

CRS.Simple
CRS 代表坐標參考系統,這是地理學家用來解釋坐標向量中坐標含義的術語。例如,如果使用地球上的緯度-經度,[15, 60]
代表印度洋中的一個點,或者在我們的星圖中代表克魯格-Z 星系。
一個 Leaflet 地圖只有一個 CRS (而且僅能有一個 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
中,一個水平地圖單位對應到一個水平像素,垂直方向亦然。這表示整個地圖大約為 1000x1000 像素大小,無法容納在我們的 HTML 容器中。幸運的是,我們可以將 minZoom
設定為小於零的值
var map = L.map('map', {
crs: L.CRS.Simple,
minZoom: -5
});
像素 vs. 地圖單位
使用 CRS.Simple
時,一個常見的錯誤是假設地圖單位等於影像像素。在本例中,地圖覆蓋 1000x1000 個單位,但影像為 2315x2315 像素大小。不同的情況會要求一個像素 = 一個地圖單位,或 64 個像素 = 一個地圖單位,或任何其他情況。請以網格中的地圖單位思考,然後據此添加您的圖層(L.ImageOverlay
、L.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
您會注意到 Sol 的坐標是 [145,175]
而不是 [175,145]
,地圖中心也會發生同樣的情況。CRS.Simple
中的坐標採用 [y, x]
的形式,而不是 [x, y]
,就像 Leaflet 使用 [lat, lng]
而不是 [lng, lat]
的方式一樣。
(在技術上,Leaflet 偏好使用[northing, easting]
而不是 [easting, northing]
- 坐標對中的第一個坐標指向「北方」,第二個指向「東方」)
關於 [lng, lat]
或 [lat, lng]
或 [y, x]
或 [x, y]
的爭論並非新鮮事,而且沒有明確的共識。缺乏共識是為什麼 Leaflet 有一個名為 L.LatLng
的類別,而不是更易混淆的 L.Coordinate
的原因。
如果使用名為 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);
地圖看起來幾乎相同,但程式碼的可讀性更高
請參閱此獨立範例。 |