利用proj4和proj4Leaflet进行坐标转化的案例分享

本文结合工作案例介绍proj4和proj4Leaflet插件用法。案例需求是用原生JS实现地图加载、切换图层,地图参数和图层可配置。先下载必要文件,接着定义参数并初始化地图,最后加载地图,还介绍了坐标转换和自定义参考系等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要玩好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&ltype=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);

代码之后上传,有用到的可以自取

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值