一文讲清楚地图地理坐标系

news2024/11/15 7:12:53

在这里插入图片描述

前言

我最近在做一个和地图有关的项目,这里本人地图采用的是mapbox,其中涉及一个功能需要根据用户输入的地点直接定位到地图上的对应的位置,本人开始想的是直接调用百度的接口根据地名直接获取坐标,发现在地图上的位置有偏移不够准确,后面发现地图服务之间有自己的坐标系,不同的坐标系之间的坐标会有一定的偏移。

地图的地理坐标系

是一种球面坐标,是使用三维球面来定义地球表面位置,以实现通过经纬度对地球表面点位引用的坐标系,也就是说坐标单位是经纬度。
在这里插入图片描述

地球坐标系(WGS84)

GPS,WGS-84,原始坐标体系。一般用国际标准的GPS记录仪记录下来的坐标,都是GPS的坐标。也是国际地图提供商使用的坐标系。但是在中国,任何一个地图产品都不允许使用GPS坐标,据说是为了保密。

使用该坐标系的在线地图

wgs84是国际标准坐标系,谷歌国外地图、osm地图等国外地图一般是这个

火星坐标系(GCJ-02)

国测局02年发布的坐标体系。又称“火星坐标”。中国标准,从国行移动设备中定位获取的坐标数据使用这个坐标系。在中国,必须至少使用GCJ-02的坐标体系。

使用该坐标系的在线地图

IOS地图、谷歌国内地图、高德地图、腾讯地图

百度坐标系(BD-09)

百度又在火星坐标上来个二次加密,只适用于百度地图

使用该坐标系的在线地图

只有百度地图
在这里插入图片描述

天地图坐标系(CGCS-2000)

天地图采用的是CGCS-2000坐标系(2000坐标系),但是因为CGCS-2000坐标系它与WGS84坐标系都是地心坐标系,因此两者相差不大。
天地图官网:https://map.tianditu.gov.cn/
在这里插入图片描述

坐标系转换

如果像我这种情况一样地图是wgs84,却用了百度地图接口返回的坐标来定位,可以对不同坐标系的坐标进行转换。下面是转换代码:

const x_pi = 3.14159265358979324 * 3000.0 / 180.0;
const pi = 3.1415926535897932384626;
const a = 6378245.0;
const ee = 0.00669342162296594323;

/**
 * 百度坐标系(BD-09)转WGS坐标
 * @param {number} lng - 百度坐标纬度
 * @param {number} lat - 百度坐标经度
 * @returns {Array} - WGS84坐标数组 [lng, lat]
 */
function bd09towgs84(lng, lat) {
    const gcj = bd09togcj02(lng, lat);
    const wgs84 = gcj02towgs84(gcj[0], gcj[1]);
    return wgs84;
}

/**
 * WGS坐标转百度坐标系(BD-09)
 * @param {number} lng - WGS84坐标系的经度
 * @param {number} lat - WGS84坐标系的纬度
 * @returns {Array} - 百度坐标数组 [lng, lat]
 */
function wgs84tobd09(lng, lat) {
    const gcj = wgs84togcj02(lng, lat);
    const bd09 = gcj02tobd09(gcj[0], gcj[1]);
    return bd09;
}

/**
 * 火星坐标系(GCJ-02)转百度坐标系(BD-09)
 * @param {number} lng - 火星坐标经度
 * @param {number} lat - 火星坐标纬度
 * @returns {Array} - 百度坐标数组 [lng, lat]
 */
function gcj02tobd09(lng, lat) {
    const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi);
    const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi);
    const bd_lng = z * Math.cos(theta) + 0.0065;
    const bd_lat = z * Math.sin(theta) + 0.006;
    return [bd_lng, bd_lat];
}

/**
 * 百度坐标系(BD-09)转火星坐标系(GCJ-02)
 * @param {number} bd_lon - 百度坐标经度
 * @param {number} bd_lat - 百度坐标维度
 * @returns {Array} - 火星坐标数组 [lng, lat]
 */
function bd09togcj02(bd_lon, bd_lat) {
    const x = bd_lon - 0.0065;
    const y = bd_lat - 0.006;
    const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
    const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
    const gg_lng = z * Math.cos(theta);
    const gg_lat = z * Math.sin(theta);
    return [gg_lng, gg_lat];
}

/**
 * WGS84转GCJ02(火星坐标系)
 * @param {number} lng - WGS84坐标系的经度
 * @param {number} lat - WGS84坐标系的纬度
 * @returns {Array} - 火星坐标数组 [lng, lat]
 */
function wgs84togcj02(lng, lat) {
    if (out_of_china(lng, lat)) {
        return [lng, lat];
    }
    let dlat = transformlat(lng - 105.0, lat - 35.0);
    let dlng = transformlng(lng - 105.0, lat - 35.0);
    const radlat = lat / 180.0 * pi;
    let magic = Math.sin(radlat);
    magic = 1 - ee * magic * magic;
    const sqrtmagic = Math.sqrt(magic);
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
    dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
    const mglat = lat + dlat;
    const mglng = lng + dlng;
    return [mglng, mglat];
}

/**
 * GCJ02(火星坐标系)转GPS84
 * @param {number} lng - 火星坐标系的经度
 * @param {number} lat - 火星坐标系纬度
 * @returns {Array} - WGS84坐标数组 [lng, lat]
 */
function gcj02towgs84(lng, lat) {
    if (out_of_china(lng, lat)) {
        return [lng, lat];
    }
    let dlat = transformlat(lng - 105.0, lat - 35.0);
    let dlng = transformlng(lng - 105.0, lat - 35.0);
    const radlat = lat / 180.0 * pi;
    let magic = Math.sin(radlat);
    magic = 1 - ee * magic * magic;
    const sqrtmagic = Math.sqrt(magic);
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
    dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
    const mglat = lat + dlat;
    const mglng = lng + dlng;
    return [lng * 2 - mglng, lat * 2 - mglat];
}

/**
 * 纬度转换
 * @param {number} lng
 * @param {number} lat
 * @returns {number}
 */
function transformlat(lng, lat) {
    let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
    ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;
    return ret;
}

/**
 * 经度转换
 * @param {number} lng
 * @param {number} lat
 * @returns {number}
 */
function transformlng(lng, lat) {
    let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
    ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;
    return ret;
}

/**
 * 判断是否在国内,不在国内不做偏移
 * @param {number} lng
 * @param {number} lat
 * @returns {boolean}
 */
function out_of_china(lng, lat) {
    if (lng < 72.004 || lng > 137.8347) {
        return true;
    } else if (lat < 0.8293 || lat > 55.8271) {
        return true;
    }
    return false;
}

当然了,最好还是在开发前先调研清楚地图的坐标系,并去使用相同坐标系的地图接口服务。

总结

这是本人第一次开发地图相关功能,也不太了解坐标系这一概念,导致在开发定位功能时位置有所偏差,经过这次学习,了解到了地图的地理坐标系的概念以及不同地理坐标系的区别,才发现定位位置偏移的问题所在,以后开发功能前也需要更详细的去调研每块领域的相关知识。

参考
https://www.jianshu.com/p/a5ebaa859ca1
http://t.csdn.cn/IiuP2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/811960.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++——模板的作用2:特例化

目录 模板的形式&#xff1a; 一.模板的多参数应用&#xff1a; 例&#xff1a; 错误使用1&#xff1a;使用不标准的模板形参表 ​编辑 错误使用2&#xff1a;使用变量作为实参传递给函数模板 二.模板的特例化&#xff1a; 类模板&#xff1a; 针对模板的特化步骤&am…

乌班图22.04安装wireguard实现异地组网

1. 前言&#xff1a; wireguard是新型的异地组网工具&#xff0c;在此之前&#xff0c;又已经被抛弃的pptp&#xff0c;l2tp&#xff0c;有配置复杂的openvpn&#xff0c;wireguard被linux作者linus赞叹优雅&#xff0c;于linux 5.6合并至linux内核主线。 2. 安装过程&#…

文心一言 VS 讯飞星火 VS chatgpt (68)-- 算法导论6.5 7题

文心一言 VS 讯飞星火 VS chatgpt &#xff08;68&#xff09;-- 算法导论6.5 7题 七、试说明如何使用优先队列来实现一个先进先出队列&#xff0c;以及如何使用优先队列来实现栈(队列和栈的定义见 10.1 节。) 文心一言&#xff1a; 优先队列是一种数据结构&#xff0c;其中…

干货|一次完整的性能测试,测试人员需要做什么?

作者介绍 邓宝菊&#xff08;Kiki Deng&#xff09;&#xff0c;10 年软件测试经验&#xff0c;4 年团队管理经验&#xff0c;当前任职研发部架构质量工程部&#xff0c;整体负责研发部测试团队的效能、工具流程建设和人才培养。 前言 一、 规范性能测试实施流程的意义 规范…

Qt: 查看qmake相关参数设置

Qt开发中&#xff0c;经常会遇到qmake相关问题&#xff0c;比如同时安装了多个Qt版本的情况。比如我的情况是系统自带了Qt 5.12.8, 但是开发中遇到一些兼容性问题&#xff0c;于是又手动安装了5.9.8。 查看qmake版本&#xff0c;qmake -v, 虽然项目中已经指定了5.9.8, 但是系统…

通过String字符生成base64编码在生成图片

* base64转图片 //对字节数组字符串进行Base64解码并生成图片 * param base64str base64码 * return // param savePath 图片路径private static final String savePath"image_ver\\verifyCode"; 判断是否为base64编码 public static void mainDD…

关闭深度学习训练/推理进程的方法

引言 设想有一种情况&#xff0c;在ssh服务器训练/推理的时候&#xff0c;突然断线&#xff0c;再次打开ssh的时候&#xff0c;发现后台在运行&#xff0c;此时无法使用 ctrlc 终止&#xff0c;从而&#xff0c;可以用一个很简单的办法来结束&#xff1a;手动关闭进程。 方法 输…

python中如何记录日志?

日志是一种可以追踪某些软件运行时所发生事件的方法。一条日志信息对应的是一个事件的发生&#xff0c;而一个事件通常需要包括以下几个内容&#xff1a;事件发生时间、事件发生位置、事件的严重程度--日志级别、事件内容。 logging模块定义的函数和类为应用程序和库的开发实现…

Python-Python基础综合案例——数据可视化 - 动态柱状图

版本说明 当前版本号[20230729]。 版本修改说明20230729初版 目录 文章目录 版本说明目录知识总览图Python基础综合案例——数据可视化 - 动态柱状图案例效果基础柱状图通过Bar构建基础柱状图反转x和y轴将数值标签放在右侧 基础时间线柱状图创建时间线自动播放时间线设置主题…

基于FPGA的VGG16卷积神经网络加速器--WL

VGG16是一个典型的卷积神经网络&#xff0c;由13层卷积层&#xff0c;5层池化层和3层全连接层组成。且卷积层的计算时间在整个计算过程中占比极大&#xff0c;通过FPGA的并行运算可以有效的加快卷积层的计算速度。 一个卷积层可以有若干个卷积核&#xff0c;以第一层为例&#…

NetSuite 工单超额完工

用户问题 在报工时&#xff0c;完工数量能否超过工单上的计划数量&#xff1f; 解决方法 在制造相关的参数中&#xff0c;有一个参数控制: ALLOW OVERAGE ON WORK ORDER TRANSACTIONS •Check On允许超出工单计划数量 •Check Off不允许超出工单计划数量 Demo 工单计划数量…

前缀和拆分

前缀和 前缀和&#xff1a;一段序列里的前n项和 给出n个数&#xff0c;在给出q次问询&#xff0c;每次问询给出L、R&#xff0c;快速求出每组数组中一段L至R区间的和 给出一段数组&#xff0c;每次问询为求出l到r区间的和 普通方法&#xff1a;L到R进行遍历&#xff0c;那么…

Python-ElasticSearch客户端的封装(聚合查询、统计查询、全量数据)

目录 ES Python客户端介绍封装代码测试代码参考 ES Python客户端介绍 官方提供了两个客户端elasticsearch、elasticsearch-dsl pip install elasticsearchpip install elasticsearch-dsl第二个是对第一个的封装&#xff0c;类似ORM操作数据库&#xff0c;可以.filter、.group…

EverEdit的一些特殊使用教学(持续更新)

项目场景&#xff1a; EverEdit的使用经常一问三不知 搜也搜不到 解决方案&#xff1a; 先去EverEdit在线帮助文档看一下(附链接) EverEdit在线帮助文档 1.快速排序 使用快捷键时:若是小键盘&#xff0c;请同时按住fn键&#xff08;在最左下角的ctrl旁&#xff09;

220. 存在重复元素 III

220. 存在重复元素 III 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;TreeSetsequenceSet.ceilingjava中的红黑树 参考代码&#xff1a; 原题链接&#xff1a; 220. 存在重复元素 III https://leetcode.cn/problems/contains-duplicate-iii/description/ 完…

ER系列路由器多网段划分设置指南

ER系列路由器多网段划分设置指南 - TP-LINK 服务支持 TP-LINK ER系列路由器支持划分多网段&#xff0c;可以针对不同的LAN接口划分网段&#xff0c;即每一个或多个LAN接口对应一个网段&#xff1b;也可以通过一个LAN接口与支持划分802.1Q VLAN的交换机进行对接&#xff0c;实现…

幅度调制与角度调制

文章目录 前言一、调制简介1、调制定义2、调制目的3、调制的分类 二、幅度调制&#xff08;线性调制&#xff09;1、幅度调制的一般模型2、常规双边带调幅 AM①、AM 信号的产生②、AM 调制器的模型③、AM 波形和频谱④、AM 信号的特点⑤、AM 包络检波⑥、调幅系数 3、抑制载波双…

Kotlin~Facade

概念 又称门面模式&#xff0c;为复杂系统提供简单交互接口。 角色介绍 Facade&#xff1a;外观类&#xff0c;供客户端调用&#xff0c;将请求委派给响应的子系统。SubSystem&#xff1a;子系统&#xff0c;独立的子设备或子类 UML 代码实现 class Light(val name: Strin…

Oracle 19c 报ORA-704 ORA-01555故障处理---惜分飞

异常断电导致数据库无法启动,尝试对数据文件进行recover操作,报ORA-00283 ORA-00742 ORA-00312错误,由于redo写丢失无法正常应用 D:\check_db>sqlplus / as sysdba SQL*Plus: Release 19.0.0.0.0 - Production on 星期日 7月 30 07:49:19 2023 Version 19.3.0.0.0 Copyrig…

msvcp120.dll丢失的解决方法?哪种解决方法比较推荐?

msvcp120.dll是Microsoft Visual C Redistributable软件包的一部分。它是用于支持运行使用Microsoft Visual C编写的应用程序的动态链接库文件。msvcp120.dll提供了许多C标准库函数和组件&#xff0c;包括输入/输出、字符串处理、数学运算、内存管理等功能。 当您运行某个依赖于…