Leaflet

一个开源 JavaScript 库
用于移动友好的交互式地图

← 教程

非地球

有时,地图并不代表地球表面的事物,因此没有地理纬度和经度的概念。大多数情况下,这指的是大型扫描图像,例如游戏地图。

在本教程中,我们选取了来自《星际控制 II》的游戏地图,该游戏现在已作为开源项目 The Ur-Quan Masters提供。这些地图使用读取游戏开源数据文件的工具制作(网页似乎已下线,请参阅存档版本),如下所示


该游戏内置了一个方形坐标系,如角落所示。这将使我们能够建立一个坐标系。


CRS.Simple

CRS 代表坐标参考系统,地理学家用它来解释坐标向量中的坐标含义。例如,[15, 60]表示地球上使用经纬度时的印度洋中的一个点,或者我们星图中的太阳系 Krueger-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
});

像素与地图单位

使用 CRS.Simple 时,一个常见的错误是假设地图单位等于图像像素。在这种情况下,地图覆盖了 1000x1000 个单位,但图像大小为 2315x2315 像素。不同的情况会要求一个像素 = 一个地图单位,或者 64 个像素 = 一个地图单位,或者任何值。以地图单位思考,然后根据需要添加图层(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

你会注意到 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);

地图看起来基本相同,但代码更易读

查看此独立示例。