扩展Leaflet:图层(layer)

时间:2021-01-29 04:12:56

        在Leaflet中,图层(layer)是随地图移动而移动的任何东西。本文首先解释如果做简单的扩展,然后看下如果创建它。

扩展方法(Extension methods)

        一些Leaflet类拥有所谓的“扩展方法(extension  methods)”:为子类编写代码的入口。

        其中一个就是L.TileLayer.getTileUrl()。无论何时当新的tile需要知道加载哪个图时就会在L.TileLayer内部调用该方法。通过创建L.TileLayer的子类和重写getTileUrl()方法,我们能够创建定制的行为。

        让我们用一个定制的L.TileLayer为例进行说明,该L.TileLayer可以展示随机的小猫图片。

L.TileLayer.Kitten = L.TileLayer.extend({
getTileUrl: function(coords) {
var i = Math.ceil( Math.random() * 4 );
return "http://placekitten.com/256/256?image=" + i;
},
getAttribution: function() {
return "<a href='http://placekitten.com/attribution.html'>PlaceKitten</a>"
}
});

L.tileLayer.kitten = function() {
return new L.TileLayer.Kitten();
}

L.tileLayer.kitten().addTo(map);

        通常getTileLayer()获取瓦片坐标(比如coords.x、coords.y和coords.z)并生成一个瓦片URL。在我们的例子中,我们忽略这些并且仅仅使用一些随机数来每次获取一个不同的小猫。

分离插件代码(Splitting away the plugin code)

        在之前的例子中,L.TileLayer.Kitten定义在了和使用位置相同的地方。对于插件而言,将插件代码分离到自己的文件中,然后在使用的时候再包含是一种更好的方式。

        对于KittenLayer,你应该创建一个类似L.KittenLayer.js文件,在该文件中写入如下代码:

L.TileLayer.Kitten = L.TileLayer.extend({
getTileUrl: function(coords) {
var i = Math.ceil( Math.random() * 4 );
return "http://placekitten.com/256/256?image=" + i;
},
getAttribution: function() {
return "<a href='http://placekitten.com/attribution.html'>PlaceKitten</a>"
}
});

        然后在展示地图的时候包含这个文件:

<html>

<script src='leaflet.js'>
<script src='L.KittenLayer.js'>
<script>
var map = L.map('map-div-id');
L.tileLayer.kitten().addTo(map);
</script>

L.GripLayer和DOM元素

        另外一种扩展方式是L.GridLayer.createTile()。L.TileLayer假设存在一系列网格图片(比如<img>元素),L.GridLayer并不这样认为,它允许创建任意的一系列HTML网格元素。

        L.GridLayer允许创建除了<div>、<canvas>或者<picture>(或者任意东西)之外的一系列网格<img>元素。createTile()仅仅返回给定瓦片坐标的HTML元素实例。知晓如果在DOM中操作元素是很重要的:Leaflet能够处理HTML元素实例,因此使用诸如JQuery等JS库创建的元素,Leaflet处理起来存在一些问题。

        定制的GridLayer的一个实例是在div中展示瓦片坐标。这对于Leaflet内部调试和理解瓦片坐标如何工作特别有用。

        

L.GridLayer.DebugCoords = L.GridLayer.extend({
createTile: function (coords) {
var tile = document.createElement('div');
tile.innerHTML = [coords.x, coords.y, coords.z].join(', ');
tile.style.outline = '1px solid red';
return tile;
}
});

L.gridLayer.debugCoords = function(opts) {
return new L.GridLayer.DebugCoords(opts);
};

map.addLayer( L.gridLayer.debugCoords() );
        如果元素需要进行一些异步的初始化,那么使用第二个函数参数done 然后当瓦片已经就绪(比如当一张图片已经完全加载)或者存在错误时调用它。这里我们仅仅人为的延迟瓦片加载。

createTile: function (coords, done) {
var tile = document.createElement('div');
tile.innerHTML = [coords.x, coords.y, coords.z].join(', ');
tile.style.outline = '1px solid red';

setTimeout(function () {
done(null, tile); // Syntax is 'done(error, tile)'
}, 500 + Math.random() * 1500);

return tile;
}

        使用定制的GridLayer,插件可以有效的控制HTML元素组成表格。一些插件已经采用该方式使用<canvas>实现了高级渲染。

        一个很基础的<canva> GridLayer是下面这样的:

L.GridLayer.CanvasCircles = L.GridLayer.extend({
createTile: function (coords) {
var tile = document.createElement('canvas');

var tileSize = this.getTileSize();
tile.setAttribute('width', tileSize.x);
tile.setAttribute('height', tileSize.y);

var ctx = tile.getContext('2d');

// Draw whatever is needed in the canvas context
// For example, circles which get bigger as we zoom in
ctx.beginPath();
ctx.arc(tileSize.x/2, tileSize.x/2, 4 + coords.z*4, 0, 2*Math.PI, false);
ctx.fill();

return tile;
}
});

像素起点(The pixel orgin)

 

        创建定制的L.Layer是可行的,但是需要深入了解Leaflet如果放置HTML元素,下面是简要说明:

  • L.Map容器拥有“地图窗格”,也就是<div>
  • L.Layer是地图窗格中的HTML元素
  • 地图将所有的LatLng转换为地图上CRS中的坐标,然后将其在转换为绝对的“像素坐标”(CRS的起点和像素坐标的起点相同)
  • 当L.Map就绪时(拥有一个中心LatLng和一个缩放水平),左上角的绝对像素坐标就成为“像素起点”
  • 每个L.Layer都是根据像素起点和图层的LatLng的绝对像素坐标从其地图窗格的偏移
  • 像素起点会在L.Map上发生的每个zoomend或者viewreset事件之后重置,并且每个L.Layer如果有必要还需重新计算其位置
  • 像素起点不会在摇晃地图时(when panning the map around)重置,而是将整个窗格重新定位

这可能让人感到困惑,因此我们来分析一下如下的说明图:


(未完。。。)


原文地址:http://leafletjs.com/examples/extending/extending-2-layers.html