I need to draw a geo-projected circle on the map. e.g. I want the center to be defined in terms of latitude and longitude and its radius to be defined in meters. I am using KDE Marble
. In the API there is a function drawEllipse
that takes a center as (lat, lon) and width and height of the ellipse.
GeoPainter::drawEllipse(const GeoDataCoordinates& center, qreal width, qreal height, bool isGeoProjected = false)
For a geo projected ellipse the width and height are considered to be in degrees. However I need them to be in meters. There is no simple conversion between degrees and meters because it depends on the position of the center in globe.
I need to convert the radius of the circle (in meters) to a pair of degree of displacement of a vector pointing to the center from the center of the earth.
I am also using boost geometry to do other geometrical calculation. Is there any function in boost-geometry
that performs this conversion ?
UPDATE I
I first tried to have two GeoDataCoordinates one is the center and the other is on the perimeter. I expected that the difference between their latitudes and longitudes will work with the drawEllipse
function.
painter->drawEllipse(_center, std::abs(_border.longitude(GeoDataCoordinates::Degree)-_center.longitude(GeoDataCoordinates::Degree)), std::abs(_border.latitude(GeoDataCoordinates::Degree)-_center.latitude(GeoDataCoordinates::Degree)), true);
However it produces an ellipse way smaller that I expected. The border point which was supposed to be on the circumference is beyond the ellipse.
UPDATE II
Then I tried to use the central angle formula on 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);
The results are not much different from the previous one. However this time the ellipse is slightly larger than the previous one.
UPDATE III
The distance ratio between the arc and the sphere's great circle circumference is used to calculate the angular displacement in @ttemple's answer
painter->drawEllipse(_center, 2*(distance/earthRadiusKm)* 180.0/3.14159, 2*(distance/earthRadiusKm)* 180.0/3.14159, true);
produces a correct ellipse.
So I multiplied the angles with 2.0
with UPDATE II code and that also produced the similar (like UPDATE III) result.
UPDATE IV
But the problem with drawEllipse
is that it actually draws a polygon with much fewer points on a zoomed out state. Sometimes it looks like a Square.
So it is a better option to draw more points on the ellipse circumference so that the fence looks like a circle in a zoomed out view too. The KDE Forum Link posted in the comments does the same.
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);
However it draws a circle with much higher radius than the input. I think the snippet posted there is incomplete. There are unused conditional blocks that I ignored in my code. Also the code specifically mentions in comments that the radius of the fence has to be given in km. I did so. Also I didn't understand that math behind this. It doesn't use Earth Radius anywhere in the snippet. May be a minor correction to this code will produce a better ellipse. The math looks like some parametric curve which produces half of the ellipse. But there is no reference to the equation.
Also this works only in first quadrant where both lat and lon is positive