【191】Java8在大比例尺小范围地图上,根据wgs84坐标系的经纬度计算两个点之间的方向和距离

场景

本文代码在大比例迟、小范围的地图上测试过。这些地图一般是县、区、镇、街道等范围的,其测试效果较好。由于地图范围较小,可以把经纬度近似看作直线。

问题分析

方向一共分东、南、西、北、东北、西北、西南、东南共八个方向。一周是 360 度,360 度除以 8 等于 45 度。以输入的第一个点为原点,绕此点一周,每个方向占45度。如果第二个点和第一个点的线段落在对应的角度范围内,就是对应的方向。

在这里插入图片描述

上图中表示了方向和角度的关系。第一个点是A,第二个点是B,如图所示,B在A的东北方向。

我以第一个点A为原点,点A所在的纬度线为x轴,点A所在的经度线为y轴,可以把地图划分成四个象限。在每个象限内,第二个点B和第一个点A之间的线段与y轴的角度决定了第二个点的方向。

请看下图例子:

在这里插入图片描述

上图中以第三象限为例,标记出了对应的角度和方向的关系。

怎么求得角度的?

设第一个点是A,第二个点是B。以A为原点构建平面直角坐标系(如上图)。过B作一条与y轴垂直的直线,交点是C。显然ABC组成了一个直角三角形。利用经纬度可获取AB长度和AC长度,利用三角函数可计算角度。

代码实现

package zhangchao;

public class GeoUtils {
    /**
     * 输入两个点的wgs84坐标系的经纬度,计算距离,单位是米。
     * @param longitude1 第一个点的经度
     * @param latitude1  第一个点的纬度
     * @param longitude2 第二个点的经度
     * @param latitude2  第二个点的纬度
     * @return 两个点的距离,单位是米。
     */
    public static double calWgs84Distance(double longitude1, double latitude1,
                                          double longitude2, double latitude2) {
        double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563;
        double L = Math.toRadians(longitude2 - longitude1);
        double U1 = Math.atan((1 - f) * Math.tan(Math.toRadians(latitude1)));
        double U2 = Math.atan((1 - f) * Math.tan(Math.toRadians(latitude2)));
        double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
        double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
        double lambda = L, lambdaP, iterLimit = 100;
        double cosSqAlpha;
        double sinSigma;
        double cos2SigmaM;
        double sigma;
        double sinLambda;
        double cosLambda;
        double cosSigma;
        do {
            sinLambda = Math.sin(lambda);
            cosLambda = Math.cos(lambda);
            sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
            if(sinSigma == 0)
                return 0;
            cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
            sigma = Math.atan2(sinSigma, cosSigma);
            double sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
            cosSqAlpha = 1 - sinAlpha * sinAlpha;
            cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
//            if(isNaN(cos2SigmaM))
//                cos2SigmaM = 0;
            double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
            lambdaP = lambda;
            lambda = L + (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
        } while (Math.abs(lambda-lambdaP) > (1e-12) && --iterLimit>0);
        if(iterLimit == 0) {
            return -1;
        }
        double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
        double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
        double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
        double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
        double s = b * A * (sigma - deltaSigma);
        double fwdAz = Math.atan2(cosU2 * sinLambda, cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
        double revAz = Math.atan2(cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
        return s;
    }


    /**
     * 按照wgs84坐标系输入两个点的经纬度,计算第二个点在第一个点的什么方向。
     * 方向用 东、南、西、北、东北、西北、西南、东南
     *
     * @param longitude1 第一个点的经度
     * @param latitude1  第一个点的纬度
     * @param longitude2 第二个点的经度
     * @param latitude2  第二个点的纬度
     * @return 返回 东、南、西、北、东北、西北、西南、东南
     */
    public static String calNSEW(double longitude1, double latitude1,
                          double longitude2, double latitude2) {
        if (longitude1 == longitude2 && latitude2 > latitude1) {
            return "北";
        }
        if (longitude1 == longitude2 && latitude2 < latitude1) {
            return "南";
        }
        if (latitude1 == latitude2 && longitude2 > longitude1) {
            return "东";
        }
        if (latitude1 == latitude2 && longitude2 < longitude1) {
            return "西";
        }
        // 计算两个点之间的距离
        double dis12 = calWgs84Distance(longitude1, latitude1, longitude2, latitude2);
        // 以第一个点为原点,第一个点所在的纬度线为x轴,第一个点所在的经度线为y轴,可以把地图划分成四个象限。
        // 第一象限
        if (longitude2 > longitude1 && latitude2 > latitude1) {
            double longitude3 = longitude1;
            double latitude3  = latitude2;
            double dis13 = calWgs84Distance(longitude1, latitude1, longitude3, latitude3);
            double radians = Math.acos(dis13 / dis12);
            double angle =Math.toDegrees(radians);
            if (angle < 22.5) {
                return "北";
            } else if (angle >= 22.5 && angle <= 67.5) {
                return "东北";
            } else {
                return "东";
            }
        }
        // 第二象限
        if (longitude2 < longitude1 && latitude2 > latitude1) {
            double longitude3 = longitude1;
            double latitude3  = latitude2;
            double dis13 = calWgs84Distance(longitude1, latitude1, longitude3, latitude3);
            double radians = Math.acos(dis13 / dis12);
            double angle =Math.toDegrees(radians);
            if (angle < 22.5) {
                return "北";
            } else if (angle >= 22.5 && angle <= 67.5) {
                return "西北";
            } else {
                return "西";
            }
        }
        // 第三象限
        if (longitude2 < longitude1  && latitude2 < latitude1) {
            double longitude3 = longitude1;
            double latitude3  = latitude2;
            double dis13 = calWgs84Distance(longitude1, latitude1, longitude3, latitude3);
            double radians = Math.acos(dis13 / dis12);
            double angle =Math.toDegrees(radians);
            if (angle < 22.5) {
                return "南";
            } else if (angle >= 22.5 && angle <= 67.5) {
                return "西南";
            } else {
                return "西";
            }
        }
        // 第四象限
        if (longitude2 > longitude1  && latitude2 < latitude1) {
            double longitude3 = longitude1;
            double latitude3  = latitude2;
            double dis13 = calWgs84Distance(longitude1, latitude1, longitude3, latitude3);
            double radians = Math.acos(dis13 / dis12);
            double angle =Math.toDegrees(radians);
            if (angle < 22.5) {
                return "南";
            } else if (angle >= 22.5 && angle <= 67.5) {
                return "东南";
            } else {
                return "东";
            }
        }
        return null;
    }


	public static void main(String[] args) {
        double lon1 = 120.084856;
        double lat1 = 35.891211;
        double lon2 = 120.444538;
        double lat2 = 36.390704;
        String nsew = calNSEW(lon1, lat1, lon2, lat2);
        double distance = calWgs84Distance(lon1, lat1, lon2, lat2);
        System.out.println(nsew);
        System.out.println(distance);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

最新GitHub学生认证,可以愉快的使用Copilot了(保姆级教程)

&#x1f388;博客主页&#xff1a;&#x1f308;我的主页&#x1f308; &#x1f388;欢迎点赞 &#x1f44d; 收藏 &#x1f31f;留言 &#x1f4dd; 欢迎讨论&#xff01;&#x1f44f; &#x1f388;本文由 【泠青沼~】 原创&#xff0c;首发于 CSDN&#x1f6a9;&#x1f…

“深入理解机器学习性能评估指标:TP、TN、FP、FN、精确率、召回率、准确率、F1-score和mAP”

目录 引言 分类标准 示例&#xff1a;癌症检测 1. 精确率&#xff08;Precision&#xff09; 2. 召回率&#xff08;Recall&#xff09; 3. 准确率&#xff08;Accuracy&#xff09; 4. F1-score 5. mAP&#xff08;均值平均精度&#xff09; 总结与通俗解释 引言 机器…

电销行业获客的精准客源从哪里来的?

在电话营销行业中找到精确的客户资源对电话营销的成功至关重要。 这里有几种方法可以找到准确的客户资源&#xff1a; 1、自身数据库&#xff1a;首先&#xff0c;使用现有的客户信息数据库&#xff0c;如客户电话号码、电子邮件和地址&#xff0c;来推广和营销现有客户。 2…

django 批量 serializers listserializers

Django drf 序列化器 序列化器 扩展serializers的有用性是我们想要解决的问题。但是&#xff0c;这不是一个微不足道的问题&#xff0c;而是需要一些严肃的设计工作。— Russell Keith-Magee, Django用户组 序列化器允许把像查询集和模型实例这样的复杂数据转换为可以轻松渲染…

基于element-plus定义表单配置化

文章目录 前言一、配置化的前提二、配置的相关组件1、新建form.vue组件2、新建input.vue组件3、新建select.vue组件4、新建v-html.vue组件5、新建upload.vue组件6、新建switch.vue组件7、新建radio.vue组件8、新建checkbox.vue组件9、新建date.vue组件10、新建time-picker.vue组…

泄露35TB数据,医疗巨头Henry Schein遭受黑猫勒索组织攻击

近日&#xff0c;据Bleeping Computer 网站消息&#xff0c;BlackCat&#xff08;黑猫&#xff09;勒索软件团伙将医疗保健巨头Henry Schein 添加到了其暗网泄露网站&#xff0c;并声称其破坏了该公司的网络&#xff0c;窃取了35 TB的敏感文件&#xff0c;这些文件包括了Henry …

【算法】道路与航线(保姆级题解)

题目 农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。 他想把牛奶送到 T 个城镇&#xff0c;编号为 1∼T。 &#xff08;存在T个点&#xff09; 这些城镇之间通过 R 条道路 (编号为 1 到 R) 和 P 条航线 (编号为 1 到 P) 连接。 &#xff08;存在R条道路&#…

Bytebase 2.11.0 - 支持 OceanBase Oracle 模式

&#x1f680; 新功能 支持 OceanBase Oracle 模式。支持设置 MySQL 在线变更参数。新增项目数据库查看者的角色。 &#x1f384; 改进 支持在项目中直接选择所有用户并为之添加角色。 调整了项目页面的布局。在 SQL 编辑器中通过悬浮面板展示表和列的详情。 &#x1faa6; …

全局后置路由守卫(afterEach)

全局后置路由守卫&#xff08;afterEach&#xff09; 功能&#xff1a;每一次切换任意路由组件之后都会被调用&#xff0c;相当于在进入下一个路由组件之后设置一个权限。 使用原理 代码创建的位置&#xff1a; 在创建router之后&#xff08;const router new VueRouter&…

基于自然语言处理的结构化数据库问答机器人系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 Wechat / QQ 名片 :) 1. 项目简介 知识库&#xff0c;就是人们总结出的一些历史知识的集合&#xff0c;存储、索引以后&#xff0c;可以被方便的检索出来供后人查询/学习。QnA Maker是用于建立知识库的工具&#xff0c;使用…

JAVA IDEA 下载

超简单步骤一&#xff1a; IntelliJ IDEA 官方下载链接 点击以上链接进入下图&#xff0c;点击下载 继续点下载&#xff0c;然后等待下载完后打开安装包即可 步骤二&#xff1a; 打开下好的安装包&#xff0c;点击Browse...我们把它下载到自己喜欢的地方&#xff08;主要是别占…

Java类和对象详解

文章目录 面向对象概述类和对象类定义和使用定义使用 对象引用对象的初始化和构造构造方法默认初始化就地初始化 面向对象概述 面向对象是一种现在主流的程序设计方法&#xff0c;现如今的大部分语言都支持面向对象&#xff0c;Java的面向对象是由C的面向对象衍生而来&#xf…

Talk | 马里兰大学博士生吴曦旸:分布式多智能体强化学习在复杂交通轨迹规划中的应用

本期为TechBeat人工智能社区第545期线上Talk&#xff01; 北京时间11月09日(周四)20:00&#xff0c;马里兰大学博士生—吴曦旸的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “分布式多智能体强化学习在复杂交通轨迹规划中的应用”&#xff0c;介…

SpringBoot定时任务打成jar 引入到新的项目中后并自动执行

一、springBoot开发定时任务 ①&#xff1a;连接数据库实现新增功能 1. 引入依赖 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional> </dependency> <dependen…

阿里云竞争加剧,腾讯云双十一服务器优惠力度爆表!

腾讯云对于新客户和老客户都有相互照顾的优惠力度。特别是在今年的双十一活动中&#xff0c;腾讯云推出了一系列的优惠活动。首先&#xff0c;轻量服务器和云服务器产品的首购活动中&#xff0c;三年的云服务器仅需540元&#xff0c;这是一个非常低廉的价格。其次&#xff0c;香…

2.3.4 交换机的DHCP技术

实验2.3.4 交换机的DHCP技术 一、任务描述二、任务分析三、具体要求四、实验拓扑五、任务实施1.交换机的基本配置。2.将交换机的接口配置为trunk模式&#xff0c;并允许vlan10 和vlan20通过。3.开启交换机的DHCP功能。4.配置交换机的DHCP服务。5.配置vlan的vlanif接口的IP地址&…

【Spring】事务实现原理

在使用事务的时候需要添加EnableTransactionManagement注解来开启事务&#xff0c;Spring事务底层是通过AOP来实现的&#xff0c;所以启用事务后&#xff0c;同样会向容器中注入一个代理对象创建器&#xff0c;AOP使用的是AnnotationAwareAspectJAutoProxyCreator&#xff0c;事…

易点易动固定资产管理系统:实现财务与OA系统的无缝对接,高效管理固定资产

在现代企业经营中&#xff0c;固定资产管理是一个非常重要的环节。准确记录和管理固定资产不仅对企业的财务状况有直接影响&#xff0c;还能提高资产利用率、降低运营成本&#xff0c;并确保企业的合规性。然而&#xff0c;传统的固定资产管理方式往往存在繁琐、效率低下的问题…

计算机考研408到底有多难?25届开个好头很有必要

前言 大家好&#xff0c;我是陈橘又青&#xff0c;相信关注我的各位小伙伴们中&#xff0c;大多都是在计算机专业的大学生吧&#xff01; 每天都有许多人在后台私信我&#xff0c;问我要不要考研&#xff0c;我想说这个东西是因人而异的&#xff0c;像我本人就选择了就业&…

ADS微带单枝短截线匹配电路的仿真

ADS微带单枝短截线匹配电路的仿真 简介环境原理图过程版图过程 简介 利用ADS2020软件设计匹配电路通常有5种方法&#xff0c;本小节首先介绍如何通过“Design-Guide”进行微带单枝短截线匹配电路的设计与仿真。 环境 ADS2020 《ADS2011射频电路设计与仿真实例》 [徐兴福著][…