计算球面上任意两点间的球面距离(C++实现)

Posted 没事就要敲代码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算球面上任意两点间的球面距离(C++实现)相关的知识,希望对你有一定的参考价值。

1 预备知识

在求解此问题之前首先要明确一下几点:

(1)两点间的球面距离: 球面上两点间的最短距离,即球心与球面上两点所确定的平面与球面相交,得到一截面圆,两点间的劣弧(圆心角<180°的圆弧)就是这两点间的球面距离。

(2)任意与球面相交的平面,球截面均为一个圆,不可能出现其他截面形状。

(3)余弦定理

(4) 弦长公式

角度制下:

已知圆弧的半径 R R R,圆心角 n ° n° n°,则弧长计算公式为

l = n ° π R 180 ° l=\\cfrac{n°\\pi R}{180°} l=180°n°πR

弧度制下:

已知圆弧的半径 R R R,圆心角(弧度) θ \\theta θ,则弧长计算公式为
l = R ⋅ θ l=R·\\theta l=Rθ

2 原理描述

已知球面上两点 S 1 ( x 1 , y 1 , z 1 ) S_1(x_1,y_1,z_1) S1(x1,y1,z1) S 2 ( x 2 , y 2 , z 2 ) S_2(x_2,y_2,z_2) S2(x2,y2,z2)与球心 O ( x 0 , y 0 , z 0 ) O(x_0,y_0,z_0) O(x0,y0,z0),可以唯一确定一个平面,且三点围成一个扇形 ⌔ O S 1 S 2 ⌔OS_1S_2 OS1S2,半径为球半径 R R R

根据余弦定理,可求得扇形的圆心角 α \\alpha α

c o s α = 2 R 2 − ∣ S 1 S 2 ∣ 2 2 R 2 = 1 − ∣ S 1 S 2 ∣ 2 2 R 2 cos\\alpha=\\cfrac{2R^2-|S_1S_2|^2}{2R^2}=1-\\cfrac{|S_1S_2|^2}{2R^2} cosα=2R22R2S1S22=12R2S1S22
α = a r c c o s ( 1 − ∣ S 1 S 2 ∣ 2 2 R 2 ) \\alpha=arccos(1-\\cfrac{|S_1S_2|^2}{2R^2}) α=arccos(12R2S1S22)

其中, ∣ S 1 S 2 ∣ |S_1S_2| S1S2 为两点构成的弦长,且
∣ S 1 S 2 ∣ = ( x 2 − x 1 ) 2 + ( y 2 − y 1 ) 2 + ( z 2 − z 1 ) 2 |S_1S_2|=\\sqrt{(x_2-x_1)^2+(y_2-y_1)^2+(z_2-z_1)^2} S1S2=(x2x1)2+(y2y1)2+(z2z1)2
根据上面给出的弧长公式,即可计算出球面上任意两点间的球面距离。

3 代码实现

以球心位于坐标原点 O ( 0 , 0 , 0 ) O(0,0,0) O(0,0,0),球半径 R = 1.0 R=1.0 R=1.0 的球面为例,已知球面上两点 p 1 ( 1 , 0 , 0 ) p_1(1,0,0) p1(1,0,0) p 2 ( 0 , 0 , 1 ) p_2(0,0,1) p2(0,0,1),求这两点的球面距离。

代码:

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

//XYZ点类型结构体
struct PointXYZ
{
public:
	double x;
	double y;
	double z;
};

//球面距离求解类
class SphereDistanceCalculator
{
public:

	/**
	* @brief   :设置球半径
	* @param[I]:R(球半径)
	* @param[O]:none
	* @return  :none
	* @note    :
	**/
	void setSphereRadius(double R);

	/**
	* @brief   :设置球面两点
	* @param[I]:p1(球面第一点)
	* @param[I]:p2(球面第二点)
	* @param[O]:none
	* @return  :none
	* @note    :
	**/
	void setSpherePoints(PointXYZ p1, PointXYZ p2);

	/**
	* @brief   :计算球面距离
	* @param[I]:none
	* @param[O]:none
	* @return  :vector<double>,依次为弦长、圆心角、球面距离
	* @note    :
	**/
	vector<double> calculateSphereDistance();

private:

	double m_R;			//球半径
	bool is_setSphereRadius = false;

	PointXYZ m_p1;		//球面第一点
	PointXYZ m_p2;		//球面第二点
	bool is_setSpherePoints = false;
};

/**
* @brief   :设置球半径
* @param[I]:R(球半径)
* @param[O]:none
* @return  :none
* @note    :
**/
void SphereDistanceCalculator::setSphereRadius(double R)
{
	if (R > 0)
	{
		m_R = R;
		is_setSphereRadius = true;
	}
	else
	{
		cerr << "\\a->球体半径应为一个正数!\\n";
		system("pause");
		abort();
	}
}

/**
* @brief   :设置球面两点
* @param[I]:p1(球面第一点)
* @param[I]:p2(球面第二点)
* @param[O]:none
* @return  :none
* @note    :
**/
void SphereDistanceCalculator::setSpherePoints(PointXYZ p1, PointXYZ p2)
{
	m_p1 = p1;
	m_p2 = p2;
	is_setSpherePoints = true;
}

/**
* @brief   :计算球面距离
* @param[I]:none
* @param[O]:none
* @return  :vector<double>,依次为弦长、圆心角、球面距离
* @note    :
**/
vector<double> SphereDistanceCalculator::calculateSphereDistance()
{
	if (!is_setSphereRadius)
	{
		cerr << "\\a->请输入球半径!\\n";
		system("pause");
		abort();
	}
	if (!is_setSpherePoints)
	{
		cerr << "\\a->请输入球面上两点三维坐标!\\n";
		system("pause");
		abort();
	}
	vector<double> res;		//存放返回值的向量
	double S1S2;			//弦长
	S1S2 = sqrt(pow((m_p2.x - m_p1.x), 2) + pow((m_p2.y - m_p1.y), 2) + pow((m_p2.z - m_p1.z), 2));
	res.push_back(S1S2);

	double alpha;			//圆心角
	alpha = acos(1 - pow(S1S2 / m_R, 2) / 2);
	res.push_back(alpha);

	double sphereDistance;	//球面距离
	sphereDistance = m_R * alpha;
	res.push_back(sphereDistance);

	return res;
}

int main()
{
	const double PI = 3.14159265359;	//常量PI
	PointXYZ p1, p2;					//球面两点坐标
	p1.x = 1.0;
	p1.y = 0.0;
	p1.z = 0.0;
	p2.x = 0.0;
	p2.y = 0.0;
	p2.z = 1.0;

	SphereDistanceCalculator sdc;		//创建球面距离求解对象
	sdc.setSphereRadius(1.0);			//设置球半径
	sdc.setSpherePoints(p1关于已知两点经纬度求球面最短距离的公式推导

请教一个关于echarts地图扩展实例的问题,不甚感激

如何在更高维度的超球面上均匀分布点?

球面最优传输映射的C++实现

空间权重矩阵的那些事-球面距离权重矩阵

怎么知道经纬度算距离,