一、背景
在Leaflet默认的地图载模式中,假如没有对地图的一个范围进行限制,那就会带来一个问题,随着地图可以拖动,当地图往右拖动越多,每跨一屏(这里的一屏指地图投影后在页面上的平铺位置)经度会加360度。同理,如果往左边拖一屏,也是相应的嫌少-360度。这是目标点的一个定位问题。
第二个问题是,假如我们在,[120.146484,29.649869]这个点标一个marker,你会发现,当拖动一屏后,它并没有在同一个位置也标上这个点。如果把此时的地图给用户看的话,肯定会产生一种错觉。
二、问题展示
1、经度大于180度的场景
使用鼠标拖拽地图到右边,使用鼠标选点展示坐标可以看到,此时坐标的经度468.984375,明显超出了正常经度的范围值。
2、经度小于-180度的场景
使用鼠标拖拽地图到左边,使用鼠标选点展示坐标可以看到,此时坐标的经度 -241.171875,明显超出了正常经度的范围值。
3、marker标错
使用如下代码进行一个marker的标定,你会发现,无论将地图往左边拖或者右边拖,地图上始终只有一个点。
L.marker([22.024546, 110.654297]).addTo(mymap) .bindPopup("<b>Hello world!</b><br/>我是一个提示框.").openPopup();
三、原因分析
造成以上问题的原因是什么呢?众所周知,在地理信息系统中,对地理范围是有一个明确的定义的。经度范围是0-180°,纬度范围是0-90°。从0°经线算起,向东、向西各分作180°,以东的180°属于东经,习惯上用“百E”作代号,以西的180°属于西经,习惯上用“W”作代号。我们通常说的纬度度指的是大地纬度。其数值在0至90度之间。位于赤道以北的点的纬度叫北纬,记为N;位于赤道以南的点的纬度称南纬,记为S。为了研究问题方便,人们把纬度分问为低、 中、高纬度。0°~30°为低纬度, 30°~ 60°为中纬度, 60~90°为高纬度。
了解了上述的知识后就知道了解决上述问题的办法,只需要在Leaflet中定义地图时,限制以下地图的经纬度范围。
四、Leaflet API
通过查找Leaflet的api我们发现了,在地图参数中有以下的定义:
默认的情况下,maxBounds参数设置为空。设置此选项后,地图会将视图限制在给定的地理范围内,如果用户试图在视图之外平移,则将用户弹回。要动态设置限制,请使用 setMaxBounds方法。通过这种方式就能限制地图的最大范围。
五、代码实现
1、leaflet资源引入
<head><title>经纬度投影-限制地图拖动范围</title><metacharset="utf-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"><linkrel="shortcut icon"type="image/x-icon"href="docs/images/favicon.ico"/><linkrel="stylesheet"href="/2d/leaflet/leaflet.css"/><scriptsrc="/2d/leaflet/leaflet.js?v=1.0.0"></script></head>
2、地图定义
L.CRS.CustomEPSG4326=L.extend({}, L.CRS.Earth, { code: 'EPSG:4326', projection: L.Projection.LonLat, transformation: newL.Transformation(1/180, 1, -1/180, 0.5), scale: function (zoom) { return256*Math.pow(2, zoom-1); } }); // 第一种设置方式,可行//限制地图的拖动范围是正负90到正负180,这样才合理。varcorner1=L.latLng(-90, -180); //设置左上角经纬度varcorner2=L.latLng(90, 180); //设置右下点经纬度varbounds=L.latLngBounds(corner1, corner2); //构建视图限制范varmymap=L.map('mapid',{crs:L.CRS.CustomEPSG4326,maxBounds:bounds}).setView([29.052934, 104.0625], 5);
这里的关键代码就是如下代码,通过这两个位置来限制拖动情况。地图的定义在crs这里,我使用了一个自己的转换规则,这取决你的瓦片读取方式。如果标准的WGS84投影,可以不需要这么定义,直接使用默认的方式加载即可。
var corner1 = L.latLng(-90, -180); //设置左上角经纬度 var corner2 = L.latLng(90, 180); //设置右下点经纬度 var bounds = L.latLngBounds(corner1, corner2); //构建视图限制范围
除了使用这种初始定义的方式,还可以使用setMaxBounds(args)这种函数调用来进行,效果是一样的,调用代码:mymap.setMaxBounds(bounds);这种方式对控制显得更灵活方便。
3、完整代码实例
<html><head><title>经纬度投影-限制地图拖动范围</title><metacharset="utf-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"><linkrel="shortcut icon"type="image/x-icon"href="docs/images/favicon.ico"/><linkrel="stylesheet"href="/2d/leaflet/leaflet.css"/><scriptsrc="/2d/leaflet/leaflet.js?v=1.0.0"></script></head><body><divid="mapid"style="width: 100%; height: 600px;"></div><script>L.CRS.CustomEPSG4326=L.extend({}, L.CRS.Earth, { code: 'EPSG:4326', projection: L.Projection.LonLat, transformation: newL.Transformation(1/180, 1, -1/180, 0.5), scale: function (zoom) { return256*Math.pow(2, zoom-1); } }); // 第一种设置方式,可行//限制地图的拖动范围是正负90到正负180,这样才合理。/*var corner1 = L.latLng(-90, -180); //设置左上角经纬度 var corner2 = L.latLng(90, 180); //设置右下点经纬度 var bounds = L.latLngBounds(corner1, corner2); //构建视图限制范围 var mymap = L.map('mapid',{crs:L.CRS.CustomEPSG4326,maxBounds:bounds}).setView([29.052934, 104.0625], 5);*/varcorner1=L.latLng(-90, -180); //设置左上角经纬度varcorner2=L.latLng(90, 180); //设置右下点经纬度varbounds=L.latLngBounds(corner1, corner2); //构建视图限制范围varmymap=L.map('mapid',{crs:L.CRS.CustomEPSG4326}).setView([29.052934, 104.0625], 5); //第二种设置方式,可行//mymap.setMaxBounds(bounds);L.tileLayer('http://localhost:8086/data/basemap_nowater/1_10_tms/{z}/{x}/{y}.jpg', { maxZoom: 16, attribution: 'yelangkingMap data © <a href="https://www.openstreetmap.org/">本地瓦片加载</a> contributors, '+'<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, '+'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', id: 'mapbox/streets-v11', tileSize: 256, zoomOffset: -1 }).addTo(mymap); //标签L.tileLayer('http://localhost:8086/data/basemap_nowater/1-10label/{z}/{x}/{y}.png', {maxZoom: 10,minZoom:3, id: 'mapbox/label',tileSize: 256,zoomOffset: -1 }).addTo(mymap); L.marker([22.024546, 110.654297]).addTo(mymap) .bindPopup("<b>Hello world!</b><br />我是一个提示框.").openPopup(); L.circle([29.649869, 120.146484], 99000, { color: 'red', fillColor: '#f03', fillOpacity: 0.5 }).addTo(mymap).bindPopup("我是一个圆."); L.polygon([ [32.916485, 101.601563], [30.562261, 105.556641], [34.524661, 108.149414] ]).addTo(mymap).bindPopup("我是一个多边形."); varpopup=L.popup(); functiononMapClick(e) { popup.setLatLng(e.latlng) .setContent("当前坐标为:"+e.latlng.toString()) .openOn(mymap); } mymap.on('click', onMapClick); </script></body></html>
六、最终效果
最终我们可以在界面上看到,当你想拖动地图到左边或者右边时,地图会默认弹回。这样就合理的避免了坐标太大超出范围的问题,以及点在地图上的标定问题。