要玩好proj4和proj4Leaflet,首先得先了解坐标系和投影得一些基础概念,推荐一篇我感觉写得比较好的文章以供参考:https://blog.csdn.net/angelazy/article/details/44085099
下面结合工作中的一个案例来叙述一下这两个插件的具体用法:
案例需求:使用原生js来实现地图的加载,点击按钮切换对应图层,地图初始化参数以及展示的图层可配置,下面粘贴一下成果图:
一、下载必要的文件:
leaflet文件包 + proj4.js + proj4Leaflet.js
引入proj4插件的方法有很多种:1、本地有nodeJS环境的可以直接npm run proj4来下载使用;2、也可以直接引入在线链接https://cdn.bootcdn.net/ajax/libs/proj4js/2.6.2/proj4.js;3、直接下载文件引入:https://github.com/worlddai/proj4leaflet,或者复制源码。
二、参数定义及初始化地图:
项目结构如下:
1、在main.html中代码如下:注意js代码引入顺序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../leaflet/leaflet.js"></script>
<!-- <script src="https://cdn.bootcdn.net/ajax/libs/proj4js/2.6.2/proj4-src.js"></script>-->
<script src="../js/proj4.js"></script>
<script src="../js/proj4leaflet.js"></script>
<script>
window.Proj4js = proj4;
</script>
<link rel="stylesheet" href="../leaflet/leaflet.css" type="text/css">
<style>
html,body {
margin: 0;height: 100%;overflow: hidden;
}
#map{
height: 100%;width:100%;float: left;z-index: 100;
}
.top{
width: 50%;height: 50px;z-index: 200;position: absolute;left: calc(25%);display: table-cell;
}
.btn{
width: 100px;
height: 40px;
margin: 5px 100px;
background-color: #0078A8;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="top">
<button class="btn" onclick="changeSl(1)">行政区图</button>
<button class="btn" onclick="changeSl(2)">影像图</button>
<button class="btn" onclick="changeSl(3)">天地图</button>
</div>
</body>
<script src="../js/config.js"></script>
<script src="../js/main.js"></script>
</html>
2、在config.js里面读取本地背后文件layer.json
layer.json包括图层地址的配置、地图初始化的一些参数
{
"layers": {
"xzq": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
"xzq": "https://wprd01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=2&style=8<ype=11",
"yxtc": "https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
"tdt": "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
},
"referenceURL": "" //这里是在线url地址,可以请求到地图初始化的一些参数
}
然后再config.js里面以立即执行函数的形式发起XMLHttpRequest请求
(function loadConfig() {
this.readJSON2("../js/layer.json",(xml) => {
config = JSON.parse(xml);
window.config = config;
this.readJSON1(config.referenceURL + '?f=json',(e) => {
Reflect.set(config,'spaceReference',JSON.parse(e));
// console.log(e);
initMap("map",config.spaceReference);
addLayer(window.config.layers.f);
})
})
})()
// 异步请求方法
function readJSON1(file, callback) {
let ajax = new XMLHttpRequest();
ajax.overrideMimeType("application/json");
ajax.open("GET", file, true);
ajax.onreadystatechange = function() {
if (ajax.readyState === 4 && ajax.status == "200") {
callback(ajax.responseText);
}
}
ajax.send(null);
}
// 同步请求方法
function readJSON2(file, callback) {
let xhr = new XMLHttpRequest();
xhr.open("GET",url,false);
xhr.onload = () => {
try{
callback(xhr.response)
} catch (e) {
console.log(`${e.name}:${e.message}`)
}
}
xhr.onerror = () => {
console.log(`${xhr.status}:${xhr.statusText}`)
}
xhr.ontimeout = () => {
console.log(`${xhr.status}: ${xhr.statusText}`)
}
xhr.send(null);
}
三、加载地图:
在获取到参数之后在main.js里面初始化地图。
let map = '', proj4 = '',scales = [],prj = '';
let resolutions = [];
function initMap (mapId,mapParam) {
let initialExtent = mapParam.initialExtent;
let lods = mapParam.tileInfo.lods;
proj4 = mapParam.proj4;
prj = mapParam.spatialReference.wkt;
const mapLevel = [];
for (let i = 0; i < lods.length; i++) {
resolutions.push(lods[i].resolution);
scales.push(lods[i].scale);
mapLevel.push(lods[i].level);
}
const maxzoom = mapLevel.length - 1;
window.Proj4js.defs('customprj', proj4);
let centerPoint = [(initialExtent.xmin + initialExtent.xmax) / 2, (initialExtent.ymax + initialExtent.ymin) / 2];
// let mapBounds;
let mapOrigin;
if (initialExtent.xmax <= 180 && initialExtent.xmax >= -180) {
mapOrigin = [-180, 90];
} else {
// 定位中心点 平面坐标转换成地理坐标
centerPoint = window.Proj4js(proj4, '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs' ,centerPoint);
// centerPoint = window.Proj4js.transform(centerPoint);
mapOrigin = [-20037508.342787, 20037508.342787];
}
// 初始化地图
map = L.map(mapId, {
center: [centerPoint[1], centerPoint[0]],
zoom: 4,
zoomControl: false,
attributionControl:false,
minZoom: 1,
maxZoom: maxzoom,
// 必须属性 测量功能中可编辑需要开启编辑
editable: true,
crs: new L.Proj.CRS('customprj',proj4,
{
origin: mapOrigin,
resolutions: resolutions
})
});
}
1、在上面的代码中需要通过Proj4封装的方法,可以从某参考系下的平面坐标上的点转成另一参考系下的地理坐标点 (经纬度),
传入fromProj:源坐标系,
toProj:所用的是EPSG4326(wgs84),
coord:坐标点
有兴趣的可以自己跟着代码跑一边
2、初始化地图:
crs: new L.Proj.CRS('customprj',proj4, { origin: mapOrigin, resolutions: resolutions }) 这段代码用到了proj4Leaflet.js
自定义了一个参考系,写法固定:1、customprj:别名;2、proj4:参考系描述(+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs);3、{}:属性信息包括resolation等
也可以把参考系写成一个变量,来进行点的转换。参考:https://blog.csdn.net/aliasone/article/details/80355184
const CRS_4549 = new L.Proj.CRS('EPSG:4549',
'+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs', // EPSG:4490的PROJ.4描述
{
resolutions: [
156367.7919628329 // 0
,78183.89598141646
,39091.94799070823
,19545.973995354114
,9772.986997677057
,4886.4934988385285
,2443.2467494192642
,1221.6233747096321
,610.8116873548161
,305.40584367740803
,152.70292183870401
,76.35146091935201
,38.175730459676004
,19.087865229838002
,9.543932614919001
,4.7719663074595005
,2.3859831537297502
,1.1929915768648751
,0.5964957884324376
,0.2982478942162188 // 19
]
}
)
// 画一个圆
L.circle(center, {radius: 100000}).addTo(map);
// 地理点单位转化
let center_latLng = L.latLng(center);
// 转平面坐标描述的点
let center_latLng_project = CRS_4549.project(center_latLng);
// 输出只:L.Point {x: 670333.9079398193, y: 3470684.886947584}
console.log(center_latLng_project);
// 转经纬度描述的点
let center_latLng_project_unproject = CRS_4549.unproject(center_latLng_project);
// 输出值:L.LatLng {lat: 31.345678912291856, lng: 121.78987654308136}
// 有些点下会只有7位左右的小数和原数据匹配,渲染已经满足。
console.log(center_latLng_project_unproject);
代码之后上传,有用到的可以自取