GPS преобразовать метры в степень смещения - PullRequest
0 голосов
/ 14 декабря 2018

Мне нужно нарисовать геопроецированный круг на карте.например, я хочу, чтобы центр был определен с точки зрения широты и долготы, а его радиус - в метрах.Я использую 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);

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

ОБНОВЛЕНИЕ II

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

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

enter image description here

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

ОБНОВЛЕНИЕ III

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

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

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

enter image description here

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

ОБНОВЛЕНИЕ IV

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

enter image description here

Поэтому лучше рисовать больше точек на окружности эллипса, чтобы ограждение выглядело как круг в увеличенном виде. 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);

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

enter image description here

Также это работает только в первом квадранте, где значения lat и lon положительны

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Ответ на форуме 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 голосов
/ 15 декабря 2018

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

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