经纬度坐标为中心点生成米距离长度半径的圆形面,含java js源码+在线绘制,代码简单零依赖

news/2024/11/14 15:27:57/

文章目录

  • java版源码
  • js版源码
  • 在线绘制预览效果
  • 关于计算的精确度

前些时间在更新我的坐标边界查询工具的时候,需要用到经纬度坐标点的距离计算,和以坐标点为中心生成一个指定距离为半径的圆,搜了一下没有找到现成简单又合适的代码,于是把自己压箱底的代码翻出来了,简化完善了一下,嘿,代码量也不大,还挺好用。

本方法是通过计算得到圆上的多个坐标点,来得到的一个近似的圆形面,只要坐标点够多,这个圆就能足够圆;有了这些坐标点就很容易表示成不同的格式,比如:GeoJSON文本WKT文本Geometry实例

源自 坐标边界查询工具 开源库:https://github.com/xiangyuecn/AreaCity-Query-Geometry (github可以换成gitee),高性能的坐标数据、边界数据查询工具,Java开源程序、带http查询接口,内存占用低(1秒可查1万个以上坐标对应的城市信息)。

java版源码

public static void main(String[] args) {//计算天坛到天安门的距离System.out.println(Distance(116.410622, 39.881773, 116.397476, 39.908647));//生成天坛1公里范围的圆形面System.out.println(CreateSimpleCircleWKT(116.410622, 39.881773, 1000, 24));
}/** 计算两个坐标的距离,单位米 **/
static public double Distance(double lng1, double lat1, double lng2, double lat2) {//采用Haversine formula算法,高德地图的js计算代码,比较简洁 https://www.cnblogs.com/ggz19/p/7551088.htmldouble d=Math.PI/180;double f=lat1*d, h=lat2*d;double i=lng2*d - lng1*d;double e=(1 - Math.cos(h - f) + (1 - Math.cos(i)) * Math.cos(f) * Math.cos(h)) / 2;return 2 * 6378137 * Math.asin(Math.sqrt(e));
}/** 以坐标点为中心,简单粗略的创建一个指定半径的圆,半径单位米,pointCount为构建圆的坐标点数(比如24个点,点越多越圆,最少3个点),返回构成圆的坐标点数组 **/
static public double[][] CreateSimpleCircle(double lng, double lat, double radius, int pointCount) {//球面坐标不会算,转换成三角坐标简单点,经度代表值大约:0.01≈1km 0.1≈10km 1≈100km 10≈1000kmdouble km=radius/1000;double a=km<5?0.01 :km<50?0.1 :km<500?1 :10;double b=Distance(lng, lat, lng+a, lat);double c=Distance(lng, lat, lng, lat+a);double rb=radius/b*a;double rc=radius/c*a;double[][] arr=new double[pointCount+1][];double n=0,step=360.0/pointCount,N=360-step/2; //注意浮点数±0.000000001的差异for(int i=0;n<N;i++,n+=step){double x=lng+rb*Math.cos(n*Math.PI/180);double y=lat+rc*Math.sin(n*Math.PI/180);arr[i]=new double[] { x, y };}arr[pointCount]=new double[] { arr[0][0], arr[0][1] }; //闭环return arr;
}/**
以坐标点为中心,简单粗略的创建一个指定半径的圆,半径单位米,pointCount为构建圆的坐标点数(比如24个点,点越多越圆,最少3个点),返回圆的WKT(Well Known Text)文本
,WKT图形绘制预览工具:https://xiangyuecn.gitee.io/areacity-jsspider-statsgov/assets/geo-echarts.html
**/
static public String CreateSimpleCircleWKT(double lng, double lat, double radius, int pointCount) {double[][] points=CreateSimpleCircle(lng, lat, radius, pointCount);DecimalFormat df=new DecimalFormat("0.######");StringBuilder wkt=new StringBuilder("POLYGON((");for(int i=0;i<points.length;i++) {if(i>0)wkt.append(",");wkt.append(df.format(points[i][0])+" "+df.format(points[i][1]));}wkt.append("))");return wkt.toString();
}

js版源码

//测试:计算天坛到天安门的距离
console.log(Distance(116.410622, 39.881773, 116.397476, 39.908647));
//测试:生成天坛1公里范围的圆形面
console.log(CreateSimpleCircleWKT(116.410622, 39.881773, 1000, 24));/** 计算两个坐标的距离,单位米 **/
function Distance(lng1, lat1, lng2, lat2) {//采用Haversine formula算法,高德地图的js计算代码,比较简洁 https://www.cnblogs.com/ggz19/p/7551088.htmlvar d=Math.PI/180;var f=lat1*d, h=lat2*d;var i=lng2*d - lng1*d;var e=(1 - Math.cos(h - f) + (1 - Math.cos(i)) * Math.cos(f) * Math.cos(h)) / 2;return 2 * 6378137 * Math.asin(Math.sqrt(e));
}/** 以坐标点为中心,简单粗略的创建一个指定半径的圆,半径单位米,pointCount为构建圆的坐标点数(比如24个点,点越多越圆,最少3个点),返回构成圆的坐标点数组 **/
function CreateSimpleCircle(lng, lat, radius, pointCount){//球面坐标不会算,转换成三角坐标简单点,经度代表值大约:0.01≈1km 0.1≈10km 1≈100km 10≈1000kmvar km=radius/1000;var a=km<5?0.01 :km<50?0.1 :km<500?1 :10;var b=Distance(lng, lat, lng+a, lat);var c=Distance(lng, lat, lng, lat+a);var rb=radius/b*a;var rc=radius/c*a;var arr=[];var n=0,step=360.0/pointCount,N=360-step/2; //注意浮点数±0.000000001的差异for(var i=0;n<N;i++,n+=step){var x=lng+rb*Math.cos(n*Math.PI/180);var y=lat+rc*Math.sin(n*Math.PI/180);arr[i]=[x, y];}arr.push([arr[0][0], arr[0][1]]); //闭环return arr;
};/**
以坐标点为中心,简单粗略的创建一个指定半径的圆,半径单位米,pointCount为构建圆的坐标点数(比如24个点,点越多越圆,最少3个点),返回圆的WKT(Well Known Text)文本
,WKT图形绘制预览工具:https://xiangyuecn.gitee.io/areacity-jsspider-statsgov/assets/geo-echarts.html
**/
function CreateSimpleCircleWKT(lng, lat, radius, pointCount){var points=CreateSimpleCircle(lng, lat, radius, pointCount);var wkt=["POLYGON(("];for(var i=0;i<points.length;i++) {wkt.push((i>0?",":"")+(+points[i][0].toFixed(6))+" "+(+points[i][1].toFixed(6)));}wkt.push("))");return wkt.join("");
};

在线绘制预览效果

生成了圆形面的WKT文本后,可以粘贴进在线预览页面绘制显示:https://xiangyuecn.gitee.io/areacity-jsspider-statsgov/assets/geo-echarts.html ,方便代码调试,地图上有测距功能,可以测量圆面的准确度;页面上的画圆功能,采用的就是js版的代码。

24个坐标点的圆形面显示效果

关于计算的精确度

两个经纬度坐标的距离计算,采用的Haversine formula算法,下面的计算代码中高德地图的api同样是Haversine formula算法,和百度地图的计算结果误差在0.2%以内:

bMap=window.BMapGL&&BMapGL.Map.prototype||{getDistance:function(a,b){return BMap.Map.prototype.getDistance(new BMap.Point(a.lng,a.lat),new BMap.Point(b.lng,b.lat)) }};//地图api计算【纬度】之间的距离,每一度之间的距离是相同的
bMap.getDistance({lng:111,lat:15},{lng:111,lat:16})        //百度111194.86米
new AMap.LngLat(111,15).distance(new AMap.LngLat(111,16)) //高德111319.49米bMap.getDistance({lng:121,lat:55},{lng:121,lat:56})        //百度111194.78米
new AMap.LngLat(121,55).distance(new AMap.LngLat(121,56)) //高德111319.49米//地图api计算【经度】之间的距离,会随着纬度的不同而不同
bMap.getDistance({lng:111,lat:15},{lng:112,lat:15})        //百度107405.91米
new AMap.LngLat(111,15).distance(new AMap.LngLat(112,15)) //高德107526.28米bMap.getDistance({lng:111,lat:55},{lng:112,lat:55})        //百度63778.21米
new AMap.LngLat(111,55).distance(new AMap.LngLat(112,55)) //高德63849.69米//经度在相同纬度下每一度之间的距离是相同的bMap.getDistance({lng:121,lat:55},{lng:122,lat:55})        //百度63778.21米new AMap.LngLat(121,55).distance(new AMap.LngLat(122,55)) //高德63849.69米

对于构成圆的坐标点的计算,使用的以前压箱底的代码,计算比较简单,经度和纬度分别计算出一度的距离长度,在等比例的换算出半径对应的度数大小,比如算出来的经度1°是100km,那么20km半径对应的度数就是1° * 20 / 100 = 0.2°

  • 纬度每一度之间的距离都是固定的长度,等比例换算后的结果是没有误差的。
  • 经度每一度之间的距离长度会随着纬度变化,低纬度长高纬度短,圆面的上下纬度不同,半径小的时候误差小,半径越大误差越大。

所以按等比例换算在纬度上没有问题,但经度上会产生一定的误差,但只要圆的半径不超过10km,误差就能控制在0.5%以内,可通过下面代码直接计算观察到:

//经度之间的距离,不同纬度下的误差计算
d1=new AMap.LngLat(111,15.0).distance(new AMap.LngLat(111.2,15.0));
d2=new AMap.LngLat(111,15.2).distance(new AMap.LngLat(111.2,15.2));
console.log(d2, (d1-d2)/d2*100+"%") //低纬度下经度0.2°距离≈20km,纬度0.2°导致0.09%误差
d1=new AMap.LngLat(111,55.0).distance(new AMap.LngLat(111.2,55.0));
d2=new AMap.LngLat(111,55.2).distance(new AMap.LngLat(111.2,55.2));
console.log(d2, (d1-d2)/d2*100+"%") //高纬度下经度0.2°距离≈10km,纬度0.2°导致0.50%误差d1=new AMap.LngLat(111,15).distance(new AMap.LngLat(112,15));
d2=new AMap.LngLat(111,16).distance(new AMap.LngLat(112,16));
console.log(d2, (d1-d2)/d2*100+"%") //低纬度下经度1°距离≈100km,纬度1°导致0.49%误差
d1=new AMap.LngLat(111,55).distance(new AMap.LngLat(112,55));
d2=new AMap.LngLat(111,56).distance(new AMap.LngLat(112,56));
console.log(d2, (d1-d2)/d2*100+"%") //高纬度下经度1°距离≈60km,纬度1°导致2.57%误差

由于是通过圆上的坐标点连起来得到的一个圆,本质上是一个近似圆的多边形,只要坐标点足够多就越接近圆,当坐标点少的情况下肉眼可见的不是那么圆(最少3个坐标点,三角形),误差也会很大。但考虑到点数越多,会导致使用上很多地方的计算量会变的很大,所以构成圆的坐标点数也是一个综合考虑的数量,我选择24个坐标点构成一个圆,每个象限6个点,点之间角度为15°。

【完】


http://www.ppmy.cn/news/274661.html

相关文章

达梦数据库与MySQL的区别及语法差异

达梦数据库与MySQL的区别及其SQL语句对比 简介&#xff1a;正文&#xff1a;1. 达梦数据库和MySQL的概述2. 特点对比3. SQL语句对比1. 数据类型&#xff1a;2. 语法&#xff1a;1. DDL&#xff08;数据定义语言&#xff09;的差异&#xff1a;2. DML&#xff08;数据操作语言&a…

object.defineproperty方法解析/自己实现一个object.defineProperty/Vue3中的Proxy解析和实现

object.defineproperty 首先说明一下 object.defineproperty不是Vue的方法&#xff0c;而是ES5中新增的方法。该方法可以用于定义对象属性的特性&#xff0c;包括可写性、可枚举、可配置性等。 该方法的代码如下&#xff1a; Object.defineProperty(obj, prop, descriptor)其…

platform驱动和pci驱动的区别

物理结构 platform驱动是虚拟总线&#xff0c;实际上硬件并不存在&#xff0c;可用于所有的硬件平台&#xff0c; pci驱动&#xff0c;是硬件上实际存在的(pci bus)&#xff0c;pci bus主要用于x86规范。 设备注册 需要手动调用platform_device_regster()函数进行注册&#…

PCI设备驱动开发

PCI总线协议 PCI&#xff08;外设部件互连标准&#xff09;总线标准是一种将系统外部设备连接起来的总线标准&#xff0c;它是PC中最重要的总线。 其他总线如ISA总线、USB等总线都挂在PCI总线之上。 PCI ( Peripheral Component Interconnect) 总线是当前最流行的总线之一&am…

PCI驱动框架简单分析

一、PCI 概念介绍 PCI是CPU和外围设备通信的高速传输总线。PCI规范能够实现32位并行数据传输&#xff0c;工作频率为 33MHz 或 66MHz &#xff0c;最大吞吐率高达266MB/s,PCI的衍生物包括 CardBus、mini-PCI、PCI-Express、cPCI等。 PCI总线体系结构是一种层次式的体系结构。在…

深入分析Linux PCI驱动框架分析(二)

说明&#xff1a; Kernel版本&#xff1a;4.14ARM64处理器使用工具&#xff1a;Source Insight 3.5&#xff0c; Visio 1. 概述 本文将分析Linux PCI子系统的框架&#xff0c;主要围绕Linux PCI子系统的初始化以及枚举过程分析&#xff1b;如果对具体的硬件缺乏了解&#xf…

Linux驱动学习--初识PCI驱动(一)

PCI是什么 PCI—Peripheral Component Interconnect&#xff0c;外围设备互联总线。是一种广泛采用的总线标准&#xff0c;它提供了许多优于其它总线标准&#xff08;如EISA&#xff09;的新特性&#xff0c;目前已经成为计算机系统中应用最为广泛&#xff0c;并且最为通用的总…

Linux驱动学习--PCI设备驱动

目录 一、引言 二、PCI总线介绍 三、PCI设备驱动组成 四、查看本机上的PCI设备 一、引言 PCI总线在linux中应用的十分广泛&#xff0c;本文就来简单介绍一下 二、PCI总线描述 1、PCI总线结构 PCI是CPU和外围设备通信的高速传输总线。普通PCI总线带宽一般为132MB/s(在3…