GPS преобразовать метры в степень смещения

Мне нужно нарисовать геопроецированный круг на карте. например Я хочу, чтобы центр определялся с точки зрения широты и долготы, а его радиус определялся в метрах. я использую KDE Marble. В API есть функция drawEllipse это занимает центр как (широта, долгота) и ширину и высоту эллипса.

GeoPainter::drawEllipse(const GeoDataCoordinates& center, qreal width, qreal height, bool isGeoProjected = false)

Для эллипса с гео-проекцией ширина и высота считаются в градусах. Однако мне нужно, чтобы они были в метрах. Нет простого преобразования между градусами и метрами, потому что это зависит от положения центра на земном шаре.

Мне нужно преобразовать радиус круга (в метрах) в пару степеней смещения вектора, указывающего на центр от центра земли.

Я также использую геометрию наддува для других геометрических расчетов. Есть ли какая-либо функция в boost-geometry что выполняет это преобразование?

ОБНОВЛЕНИЕ I

Сначала я попытался получить две координаты 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);

Однако он производит эллипс намного меньше, чем я ожидал. Граничная точка, которая должна была быть на окружности, находится за пределами эллипса.
введите описание изображения здесь

ОБНОВЛЕНИЕ II

Затем я попытался использовать формулу центрального угла на википедия

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);

введите описание изображения здесь

Результаты мало чем отличаются от предыдущего. Однако на этот раз эллипс немного больше предыдущего.

ОБНОВЛЕНИЕ III

Отношение расстояний между дугой и окружностью большой окружности сферы используется для вычисления углового смещения в ответе @ ttemple

painter->drawEllipse(_center, 2*(distance/earthRadiusKm)* 180.0/3.14159, 2*(distance/earthRadiusKm)* 180.0/3.14159, true);

производит правильный эллипс.

введите описание изображения здесь

Таким образом, я умножил углы с 2.0 с ОБНОВЛЕНИЕ II код, и это также произвело подобное (как ОБНОВЛЕНИЕ III) результат.

ОБНОВЛЕНИЕ IV

Но проблема с drawEllipse в том, что он фактически рисует многоугольник с гораздо меньшим количеством точек в уменьшенном состоянии. Иногда это выглядит как квадрат.

введите описание изображения здесь

Поэтому лучше рисовать больше точек на окружности эллипса, чтобы ограждение выглядело как круг на увеличенном изображении. KDE Forum Link размещенный в комментариях делает то же самое.

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);

Однако он рисует круг с гораздо большим радиусом, чем на входе. Я думаю, что фрагмент, размещенный там, является неполным. Есть неиспользованные условные блоки, которые я игнорировал в своем коде. Также в коде конкретно упоминается в комментариях, что радиус забора нужно указывать в км. Я так и сделал. Кроме того, я не понял, что математика за этим стоит. Он не использует Радиус Земли нигде во фрагменте. Может быть небольшая поправка к этому коду даст лучший эллипс. Математика выглядит как некоторая параметрическая кривая, которая производит половину эллипса. Но нет ссылки на уравнение.

введите описание изображения здесь

Кроме того, это работает только в первом квадранте, где и широта и долгота положительны

0

Решение

Радиус ограждения, измеренный по кривой сферы, деленный на радиус сферы, представляет собой угол в радианах.

double circular_radius_of_fence = 1000;    // circular radius of fence
double sphere_radius = 6378000;   // radius of sphere, meters

// included angle in radians
double ang_radians = circular_radius_of_fence / sphere_radius;

// convert to degrees
double ang_degrees = ang_radians * 180.0/PI;

// width and height of circle is twice the angle
double width = ang_degrees * 2;
double height = ang_degrees * 2;
0

Другие решения

Ответ в Форум 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 это радиус забора.

0

По вопросам рекламы [email protected]