c++ - GPS 将米转换为位移度
问题描述
我需要在地图上绘制一个地理投影圆。例如,我希望根据纬度和经度来定义中心,并以米为单位定义其半径。我正在使用KDE Marble
. 在 API 中有一个函数drawEllipse
,它将中心作为(纬度,经度)以及椭圆的宽度和高度。
GeoPainter::drawEllipse(const GeoDataCoordinates& center, qreal width, qreal height, bool isGeoProjected = false)
对于地理投影椭圆,宽度和高度被认为是度数。但是我需要它们以米为单位。度和米之间没有简单的转换,因为它取决于地球中心的位置。
我需要将圆的半径(以米为单位)转换为从地球中心指向中心的向量的一对位移度。
我也在使用 boost geometry 来做其他的几何计算。是否有任何功能boost-geometry
可以执行此转换?
更新我
我首先尝试有两个 GeoDataCoordinates 一个是中心,另一个是外围。我预计他们的纬度和经度之间的差异将适用于该drawEllipse
功能。
painter->drawEllipse(_center, std::abs(_border.longitude(GeoDataCoordinates::Degree)-_center.longitude(GeoDataCoordinates::Degree)), std::abs(_border.latitude(GeoDataCoordinates::Degree)-_center.latitude(GeoDataCoordinates::Degree)), true);
然而,它产生的椭圆比我预期的要小。应该在圆周上的边界点超出了椭圆。
更新二
然后我尝试使用维基百科上的中心角公式
double angular_distance = acos(
(sin(_center.latitude(GeoDataCoordinates::Radian))*sin(_border.latitude(GeoDataCoordinates::Radian)))
+(cos(_center.latitude(GeoDataCoordinates::Radian))*cos(_border.latitude(GeoDataCoordinates::Radian)))
*(double)cos(std::abs(_border.longitude(GeoDataCoordinates::Radian)-_center.longitude(GeoDataCoordinates::Radian)))
);
painter->drawEllipse(_center, stmr::internal::rad2deg(angular_distance), stmr::internal::rad2deg(angular_distance), true);
结果与上一个没有太大区别。然而,这一次椭圆比前一个略大。
更新三
圆弧与球体大圆周长之间的距离比用于计算@ttemple 答案中的角位移
painter->drawEllipse(_center, 2*(distance/earthRadiusKm)* 180.0/3.14159, 2*(distance/earthRadiusKm)* 180.0/3.14159, true);
产生一个正确的椭圆。
2.0
因此,我将角度与UPDATE II代码相乘,这也产生了类似的(如UPDATE III)结果。
更新四
但问题drawEllipse
在于它实际上在缩小状态下绘制了一个点少得多的多边形。有时它看起来像一个正方形。
因此,在椭圆圆周上绘制更多点是一个更好的选择,这样围栏在缩小视图中也看起来像一个圆圈。评论中发布的KDE 论坛链接也是如此。
GeoDataLineString ellipse;
qreal lon = 0.0;
qreal lat = 0.0;
int precision = 180;
for ( int i = 0; i < precision; ++i){
qreal t = 1.0 - 2.0 * (qreal)(i) / (qreal)(precision);
lat = center.latitude(GeoDataCoordinates::Degree) + 0.5 * radius * sqrt(1.0 - t * t);
lon = center.longitude(GeoDataCoordinates::Degree) + 0.5 * radius * t;
ellipse << GeoDataCoordinates(lon, lat, 0.0, GeoDataCoordinates::Degree);
}
for ( int i = 0; i < precision; ++i){
qreal t = 2.0 * (qreal)(i) / (qreal)(precision) - 1.0;
lat = center.latitude(GeoDataCoordinates::Degree) - 0.5 * radius * sqrt(1.0 - t * t);
lon = center.longitude(GeoDataCoordinates::Degree) + 0.5 * radius * t;
ellipse << GeoDataCoordinates(lon, lat, 0.0, GeoDataCoordinates::Degree);
}
painter->drawPolyline(ellipse);
然而,它绘制了一个半径比输入大得多的圆。我认为那里发布的片段不完整。我在代码中忽略了一些未使用的条件块。代码还特别在注释中提到围栏的半径必须以公里为单位。我这样做了。我也不明白这背后的数学。它不会在片段中的任何地方使用地球半径。可能对这个代码稍加修正会产生一个更好的椭圆。数学看起来像一些产生一半椭圆的参数曲线。但是没有提到方程。
这也仅适用于 lat 和 lon 均为正的第一象限
解决方案
KDE 论坛中的答案实际上是在GeoPainter::drawEllipse
.
const int precision = qMin<qreal>( width / degreeResolution / 8 + 1, 81 )
该实现中的这条线降低了精度,使其在缩小时看起来像一个正方形。所以我用单行修改复制了drawEllipse代码以获得更高的精度。
void FenceLayer::drawFence(Marble::GeoPainter* painter, Marble::GeoDataCoordinates center, double radius){
double height = radius;
double width = radius;
// Initialize variables
const qreal centerLon = center.longitude( GeoDataCoordinates::Degree );
const qreal centerLat = center.latitude( GeoDataCoordinates::Degree );
const qreal altitude = center.altitude();
// Ensure a valid latitude range:
if ( centerLat + 0.5 * height > 90.0 || centerLat - 0.5 * height < -90.0 ) {
return;
}
// Don't show the ellipse if it's too small:
GeoDataLatLonBox ellipseBox( centerLat + 0.5 * height, centerLat - 0.5 * height,
centerLon + 0.5 * width, centerLon - 0.5 * width,
GeoDataCoordinates::Degree );
if ( !_map->viewport()->viewLatLonAltBox().intersects( ellipseBox ) ||
!_map->viewport()->resolves( ellipseBox ) ) return;
GeoDataLinearRing ellipse;
// Optimizing the precision by determining the size which the
// ellipse covers on the screen:
const qreal degreeResolution = _map->viewport()->angularResolution() * RAD2DEG;
// To create a circle shape even for very small precision we require uneven numbers:
const int scaled_resolution = qMin<qreal>( width / degreeResolution / 8 + 1, 81 );
const int precision = qMin<qreal>( width / degreeResolution / 8 + 1, 81 ) < 81 ? 81 : scaled_resolution ;
// Calculate the shape of the upper half of the ellipse:
for ( int i = 0; i <= precision; ++i ) {
const qreal t = 1.0 - 2.0 * (qreal)(i) / (qreal)(precision);
const qreal lat = centerLat + 0.5 * height * sqrt( 1.0 - t * t );
const qreal lon = centerLon + 0.5 * width * t;
ellipse << GeoDataCoordinates( lon, lat, altitude, GeoDataCoordinates::Degree );
}
// Calculate the shape of the lower half of the ellipse:
for ( int i = 0; i <= precision; ++i ) {
const qreal t = 2.0 * (qreal)(i) / (qreal)(precision) - 1.0;
const qreal lat = centerLat - 0.5 * height * sqrt( 1.0 - t * t );
const qreal lon = centerLon + 0.5 * width * t;
ellipse << GeoDataCoordinates( lon, lat, altitude, GeoDataCoordinates::Degree );
}
painter->drawPolygon(ellipse);
}
在调用时drawFence
,我按照@ttemple 的回答中的建议将距离转换为位移程度。
drawFence(painter, _center, 2*(distance/earthRadiusKm)* 180.0/3.14159);
distance
围栏的半径在哪里。
推荐阅读
- python - Python中的图像处理-这个问题有通用的解决方案吗?
- python - 德语中带有spacy的小写词形还原
- c++ - 将 J1939 消息转换为原始 CAN
- python - 我怎样才能使python列表中的每个项目都在谷歌表格中复制一组单元格?
- python - 如何解决归并排序算法中的递归问题?
- angular - 如何在属于 formArray 的禁用输入字段中显示逗号?
- c# - 在 EF Core 3.1 的 Where () 中使用 Any()
- mysql - MySQL 在查询中 ROWNUM 的替代方法是什么?
- azure - 从 Jenkins 管道部署 Azure Webjobs
- html - Rselenium 和带有 wbr 的对象