【Leaflet + 天地图】通过经纬度、角度方向、距离(米)计算偏移后的经纬度

一、天地图

1.1 简介

百度和高德是大家经常使用的地图平台,他们的优点非常多,它们存在着广大的开源社区。但是缺点也非常明显,如不能对卫星图进行较大级别的查看,很多地区存在无法查看的现象。
那么在很多业务场景中需要我们查看清晰的卫星图,怎么办呢?这就用到了天地图。
天地图:“天地图”是国家测绘地理信息局建设的地理信息综合服务网站。它是“数字中国”的重要组成部分,是国家地理信息公共服务平台的公众版。“天地图”的目的在于促进地理信息资源共享和高效利用,提高测绘地理信息公共服务能力和水平,改进测绘地理信息成果的服务方式,更好地满足国家信息化建设的需要,为社会公众的工作和生活提供方便。
它是国家绘制地理信息局建设的,依靠着强大的背景,也就完全避免了上述两种地图的问题,但是区域仅限于中国
官网地址

数字化管理平台
Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus
个人博客

使用天地图,需要先到官网(国家地理信息公共服务平台 天地图)进行注册并登录。
在这里插入图片描述

找到开发资源->地图API,可以看到地图服务列表

找到 网页API->JavaScript API 查看相关实例和类参考。

1.2 实现天地图一个简单的渲染需要六步:

  1. 准备页面

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8"/>
        <title>HELLO WORLD</title>
        <script type="text/javascript" src="http://api.tianditu.gov.cn/api?v=4.0&tk=您的密钥"></script>
        <script>
            var map;
            var zoom = 12;
            function onLoad() {
          
          
                map = new T.Map('mapDiv');
                map.centerAndZoom(new T.LngLat(116.40769, 39.89945), zoom);
            }
        </script>
    </head>
    <body onLoad="onLoad()">
    <div id="mapDiv" style="position:absolute;width:500px; height:400px"></div>
    </body>
    </html>
    
  2. 引入天地图 JS 文件

    <script src="http://api.tianditu.gov.cn/api?v=4.0&tk=您的密钥" type="text/javascript"></script>
    
  3. 创建容器元素
    地图需要一个HTML元素作为容器,这样才能展现到页面上。这里我们创建了一个div元素。将div元素的宽和高分别设置为100%,使其充满整个屏幕,或者您也可以计算浏览器窗口的大小并进行设置。

  4. 创建地图实例

    //初始化地图对象
    var map=new T.Map('mapDiv');
    
  5. 确定经纬度坐标

    var lnglat = new T.LngLat(116.40969,39.89945)
    

    这里我们使用T命名空间下的T.Lnglat类来创建一个坐标点。T.Lnglat类描述了一个地理坐标点,其中116.40969表示经度,39.89945表示纬度。

  6. 地图初始化

    map.centerAndZoom(lnglat,12);
    

    在创建地图实例后,我们需要对其进行初始化,map.centerAndZoom方法要求设置中心点坐标和地图级别。 地图必须经过初始化才可以执行其他操作。

1.3 工具类代码

 var lineConfig = {
    
    
    showLabel: true,
    color: 'red'
};
 var lineTool = new T.PolylineTool(this.map, lineConfig); // 创建测距工具
 lineTool.open() // 打开测距工具 可以开始打点测距
 lineTool.close() ;// 关闭测距工具
 lineTool.clear() ;// 清除所有点
 var polygonConfig =  {
    
    
    showLabel: true,
    color: "red", weight: 3, opacity: 0.5, fillColor: "#FFFFFF", fillOpacity: 0.5
};
 var polygonTool = new T.PolygonTool(this.map, polygonConfig);// 创建测面工实例, 其使用方式与测距相同

1.4 信息窗体显示与隐藏

 var  infoWindow = new T.InfoWindow(); // 创建信息窗体实例
 infoWindow.setContent('<div></div>'); // 设置信息窗体内容, 也可以在vue中使用ref
 infoWindow.setLngLat(new T.LngLat(e['lnglat']['lng'], e['lnglat']['lat'])); // 设置信息窗标准点
 map.addOverLay(infoWindow);// 打开信息窗体
 map.removeOverLay(infoWindow);//关闭信息窗体

1.5 地图上标记点

 var icon = new T.Icon({
    
    
     iconUrl: imgsrc,  // 图标url
     iconSize: new T.Point(30, 40) // 图标大小
  }); // 创建标记点使用的图标
 var marker = new T.Marker(new T.LngLat(info['oriLongitude'] , info['oriLatitude']), {
    
    
     icon: icon
  })  ; // 创建标记点实例, 第一个参数为坐标点天地图经纬度, 第二个参数是配置项
  map.addOverLay(marker); // 显示此坐标点
  map.removeOverLay(marker); // 隐藏此坐标点
  marker.addEventListener("click", callback); // 绑定marker的点击事件, 这里我们可以做一些操作, 比如弹出信息窗体等

1.5 绘制线

  var line = new T.Polyline(path, {
    
    weight: 8,opacity: 0.7}); //path为天地图经纬度数组,第二个参数为配置项
  map.addOverLay(line);  // 绘制线到地图上
  map.removeOverLay(line);// 移除线

其它相关绘制如:绘制圆、多边形等详见官方文档,不再一一赘述。

二、Leaflet

2.1 简介

Leaflet.js 是目前最流行的映射库之一。它是一个灵活、轻量级的开源 JavaScript 库,用于创建交互式地图。 它大小仅仅只有 42 KB of JS, 并且拥有绝大部分开发者所需要的所有地图特性 。
Leaflet 是一个用于呈现地图数据的框架。数据以及底图图层必须由开发人员提供。地图由切片图层以及浏览器支持、默认交互性、平移和缩放功能组成。您还可以添加更多自定义图层和插件,以及 Leaflet 中的所有映射。该地图库将您的数据转换为地图图层,并提供出色的支持,使其成为大多数开发人员的首选。它在主要的桌面和移动平台上运行良好,使其成为移动和大屏幕地图的完美 JavaScript 库。

Leaflet 中文站点
Leaflet 官方站点

在这里插入图片描述
一些 Leaflet 简单的使用,如:设置 Leaflet 地图、使用标记、折线和弹出窗口,以及处理事件。
在这里插入图片描述

2.2 准备一个html页面,并引入相应的文件

  • 在文档的 head 部分引入 Leaflet CSS 文件:
 <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
     integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI="
     crossorigin=""/>
  • 在引入 Leaflet CSS 文件之后引入 Leaflet JavaScript 文件:
 <!-- Make sure you put this AFTER Leaflet's CSS -->
 <script src="https://unpkg.com/[email protected]/dist/leaflet.js"
     integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM="
     crossorigin=""></script>
  • 将具有特定 id 的 div 元素放置在你希望地图所在的位置:
<div id="map"></div>
  • 确保地图容器定义了固定高度,例如在 CSS 中设置:
#mapid {
    
     height: 180px; }

2.3 初始化地图并设置地理坐标、缩放级别

var map = L.map('map').setView([51.505, -0.09], 13);

默认情况下,地图上的所有鼠标和触摸交互都处于启用状态,并且具有缩放和属性控制。
setView 调用还会返回地图对象,事实上大多数 Leaflet 方法在不返回显式值时的行为都是这样的,这允许你可以很方便的进行类似 jQuery 的链式调用。

2.4 添加一个瓦片(Tile)图层到地图中

创建一个瓦片(Tile)图层通常会涉及为瓦片(Tile)图像、归属文本和图层的最大缩放级别设置 URL 模板。

L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
    
    
    maxZoom: 19,
    attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);

确保引入 leaflet.js 文件并配置好 div 后运行所有代码。就是这样,你现在拥有一个运行正常的 Leaflet 地图。

2.5 绘制标记、圆和多边形

  • 绘制标记点

    var marker = L.marker([51.5, -0.09]).addTo(map);
    
  • 绘制圆形

    var circle = L.circle([51.508, -0.11], {
          
          
        color: 'red',
        fillColor: '#f03',
        fillOpacity: 0.5,
        radius: 500
    }).addTo(map);
    
  • 绘制多边形

    	var polygon = L.polygon([
    	    [51.509, -0.08],
    	    [51.503, -0.06],
    	    [51.51, -0.047]
    	]).addTo(map);
    

2.6 添加 popups

在这里插入图片描述
当您想将某些信息附加到地图上的特定对象时,通常会使用 Popups。Leaflet 有一个快捷方式:

marker.bindPopup("<b>Hello world!</b><br>I am a popup.").openPopup();
circle.bindPopup("I am a circle.");
polygon.bindPopup("I am a polygon.");

尝试点击我们的对象。该 bindPopup 方法将带有指定 HTML 内容的弹出窗口附加到您的标记,以便在您单击对象时显示弹出窗口,并且该 openPopup 方法(仅适用于标记)立即打开附加的弹出窗口。

2.7 处理事件

每次在 Leaflet 中触发某些事件时,例如用户单击标记或地图缩放更改,相应的对象都会发送一个事件,您可以使用函数订阅该事件。它允许您对用户交互做出反应:

function onMapClick(e) {
    
    
    alert("You clicked the map at " + e.latlng);
}

map.on('click', onMapClick);

监听器、函数的第一个参数是一个事件对象——它包含关于发生的事件的有用信息。例如,地图点击事件对象(在上面的例子中的e)有一个 latlng 属性,它是点击发生的位置。
通过使用 popup 而不是 alert 来改进示例:

var popup = L.popup();

function onMapClick(e) {
    
    
    popup
        .setLatLng(e.latlng)
        .setContent("You clicked the map at " + e.latlng.toString())
        .openOn(map);
}

map.on('click', onMapClick);

其它详情文档
在这里插入图片描述

2.8 其它一些插件

三、根据一个经纬度及距离角度,算出另外一个经纬度

/**
  * 根据一个经纬度及距离角度,算出另外一个经纬度
  * @param {*} lng 经度 113.3960698
  * @param {*} lat 纬度 22.941386
  * @param {*} brng 方位角 45 ---- 正北方:000°或360° 正东方:090° 正南方:180° 正西方:270°
  * @param {*} dist 90000距离(米)
  * 1、角度转换为弧度公式:弧度=角度×(π ÷度180 )
  * 2、弧度转换为角度公式: 角度=弧度×(180÷π)
*/
function getLonAndLat(lng, lat, brng, dist) {
    //大地坐标系资料WGS-84 长半径a=6378137 短半径b=6356752.3142 扁率f=1/298.2572236
    var a = 6378137;
    var b = 6356752.3142;
    var f = 1 / 298.257223563;

    var lon1 = lng * 1;
    var lat1 = lat * 1;
    var s = dist;
    var alpha1 = brng * (Math.PI / 180)//mapNumberUtil.rad(brng);
    var sinAlpha1 = Math.sin(alpha1);
    var cosAlpha1 = Math.cos(alpha1);

    //var tanU1 = (1 - f) * Math.tan(mapNumberUtil.rad(lat1));
    var tanU1 = (1 - f) * Math.tan(lat1 * (Math.PI / 180));
    var cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
    var sigma1 = Math.atan2(tanU1, cosAlpha1);
    var sinAlpha = cosU1 * sinAlpha1;
    var cosSqAlpha = 1 - sinAlpha * sinAlpha;
    var uSq = cosSqAlpha * (a * a - b * b) / (b * b);
    var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
    var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

    var sigma = s / (b * A), sigmaP = 2 * Math.PI;
    while (Math.abs(sigma - sigmaP) > 1e-12) {
        var cos2SigmaM = Math.cos(2 * sigma1 + sigma);
        var sinSigma = Math.sin(sigma);
        var cosSigma = Math.cos(sigma);
        var deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
            B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
        sigmaP = sigma;
        sigma = s / (b * A) + deltaSigma;
    }

    var tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
    var lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
        (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
    var lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
    var C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
    var L = lambda - (1 - C) * f * sinAlpha *
        (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));

    var revAz = Math.atan2(sinAlpha, -tmp); // final bearing

    var lngLatObj = { lng: lon1 + L * (180 / Math.PI), lat: lat2 * (180 / Math.PI) }
    return lngLatObj;
}

猜你喜欢

转载自blog.csdn.net/qq_39335404/article/details/132274587