Google Maps Точки пересечения двух кругов
Есть ли простой способ получить lat
/lng
точек пересечения (если есть) двух окружностей в Google Maps API V3? Или я должен идти трудным путем?
РЕДАКТИРОВАТЬ: В моей проблеме, круги всегда имеют одинаковый радиус, в случае, если это облегчает решение.
3 ответа
Да, для равных кругов может быть разработано довольно простое решение:
Давайте первый центр круга - это точка A, второй центр круга - это F, средняя точка - это C, а точки пересечения - это B,D. ABC - прямоугольный сферический треугольник с прямым углом C.
Мы хотим найти угол A - это угол отклонения от направления AF. Сферическая тригонометрия (правила Нейпира для правильных сферических треугольников) дает нам формулу:
cos(A)= tg(AC) * ctg(AB)
где один символ обозначает сферический угол, двойной символ обозначает угол дуги большого круга (AB, AC). Мы можем видеть, что AB = радиус круга (в радианах, конечно), AC = половинное расстояние между A и F на дуге большого круга. Чтобы найти AC (и другие значения) - я буду использовать код с этой превосходной страницы
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
и наш
AC = c/2
Если радиус окружности Rd задан в километрах, то
AB = Rd / R = Rd / 6371
Теперь мы можем найти угол
A = arccos(tg(AC) * ctg(AB))
Начальный подшипник (направление AF):
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x);
Подшипники точек пересечения:
B_bearing = brng - A
D_bearing = brng + A
Координаты точек пересечения:
var latB = Math.asin( Math.sin(lat1)*Math.cos(Rd/R) +
Math.cos(lat1)*Math.sin(Rd/R)*Math.cos(B_bearing) );
var lonB = lon1.toRad() + Math.atan2(Math.sin(B_bearing)*Math.sin(Rd/R)*Math.cos(lat1),
Math.cos(Rd/R)-Math.sin(lat1)*Math.sin(lat2));
и то же самое для D_bearing
latB, lonB в радианах
Вычисление "сложным" способом может быть упрощено для случая r1 = r2 =: r. Сначала нам нужно преобразовать центры окружностей P1,P2 из (lat,lng) в декартовы координаты (x,y,z).
var DEG2RAD = Math.PI/180;
function LatLng2Cartesian(lat_deg,lng_deg)
{
var lat_rad = lat_deg*DEG2RAD;
var lng_rad = lng_deg*DEG2RAD;
var cos_lat = Math.cos(lat_rad);
return {x: Math.cos(lng_rad)*cos_lat,
y: Math.sin(lng_rad)*cos_lat,
z: Math.sin(lat_rad)};
}
var P1 = LatLng2Cartesian(lat1, lng1);
var P2 = LatLng2Cartesian(lat2, lng2);
Но линию пересечения плоскостей, содержащих круги, можно вычислить проще. Позволять d
быть расстоянием от фактического центра окружности (в плоскости) до соответствующей точки P1 или P2 на поверхности. Простой вывод показывает (с R радиус Земли):
var R = 6371; // earth radius in km
var r = 100; // the direct distance (in km) of the given points to the intersections points
// if the value rs for the distance along the surface is known, it has to be converted:
// var r = 2*R*Math.sin(rs/(2*R*Math.PI));
var d = r*r/(2*R);
Теперь пусть S1 и S2 - точки пересечения, а S - их средняя точка. С s = |OS|
а также t = |SS1| = |SS2|
(где O = (0,0,0) - центр Земли) мы получаем из простых выводов:
var a = Math.acos(P1.x*P2.x + P1.y*P2.y + P1.z*P2.z); // the angle P1OP2
var s = (R-d)/Math.cos(a/2);
var t = Math.sqrt(R*R - s*s);
Сейчас с r1 = r2
точки S, S1, S2 находятся в средней плоскости между P1 и P2. За v_s = OS
мы получаем:
function vecLen(v)
{ return Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z); }
function vecScale(scale,v)
{ return {x: scale*v.x, y: scale*v.y, z: scale*v.z}; }
var v = {x: P1.x+P2.x, y: P1.y+P2.y, z:P1.z+P2.z}; // P1+P2 is in the middle of OP1 and OP2
var S = vecScale(s/vecLen(v), v);
function crossProd(v1,v2)
{
return {x: v1.y*v2.z - v1.z*v2.y,
y: v1.z*v2.x - v1.x*v2.z,
z: v1.x*v2.y - v1.y*v2.x};
}
var n = crossProd(P1,P2); // normal vector to plane OP1P2 = vector along S1S2
var SS1 = vecScale(t/vecLen(n),n);
var S1 = {x: S.x+SS1.x, y: S.y+SS1.y, z: S.z+SS1.z}; // S + SS1
var S2 = {x: S.x-SS1.x, y: S.y-SS2.y, z: S.z-SS1.z}; // S - SS1
Наконец, мы должны преобразовать обратно в (lat,lng):
function Cartesian2LatLng(P)
{
var P_xy = {x: P.x, y:P.y, z:0}
return {lat: Math.atan2(P.y,P.x)/DEG2RAD, lng: Math.atan2(P.z,vecLen(P_xy))/DEG2RAD};
}
var S1_latlng = Cartesian2LatLng(S1);
var S2_latlng = Cartesian2LatLng(S2);
Язанпро, извините за поздний ответ на это.
Возможно, вас заинтересует краткий вариант подхода MBo, который упрощается в двух отношениях:
- во-первых, используя некоторые встроенные функции API google.maps, чтобы избежать сложной математики.
- во-вторых, с использованием 2D-модели для расчета включенного угла вместо сферической модели MBo. Сначала я не был уверен в правильности этого упрощения, но удовлетворился тестами на развилке скрипки MBo, что ошибки незначительны, за исключением самых больших кругов по отношению к размеру Земли (например, при низких уровнях масштабирования).
Вот функция:
function getIntersections(circleA, circleB) {
/*
* Find the points of intersection of two google maps circles or equal radius
* circleA: a google.maps.Circle object
* circleB: a google.maps.Circle object
* returns: null if
* the two radii are not equal
* the two circles are coincident
* the two circles don't intersect
* otherwise returns: array containing the two points of intersection of circleA and circleB
*/
var R, centerA, centerB, D, h, h_;
try {
R = circleA.getRadius();
centerA = circleA.getCenter();
centerB = circleB.getCenter();
if(R !== circleB.getRadius()) {
throw( new Error("Radii are not equal.") );
}
if(centerA.equals(centerB)) {
throw( new Error("Circle centres are coincident.") );
}
D = google.maps.geometry.spherical.computeDistanceBetween(centerA, centerB); //Distance between the two centres (in meters)
// Check that the two circles intersect
if(D > (2 * R)) {
throw( new Error("Circles do not intersect.") );
}
h = google.maps.geometry.spherical.computeHeading(centerA, centerB); //Heading from centre of circle A to centre of circle B. (in degrees)
h_ = Math.acos(D / 2 / R) * 180 / Math.PI; //Included angle between the intersections (for either of the two circles) (in degrees). This is trivial only because the two radii are equal.
//Return an array containing the two points of intersection as google.maps.latLng objects
return [
google.maps.geometry.spherical.computeOffset(centerA, R, h + h_),
google.maps.geometry.spherical.computeOffset(centerA, R, h - h_)
];
}
catch(e) {
console.error("getIntersections() :: " + e.message);
return null;
}
}
Кстати, нет никакого неуважения к MBo - это отличный ответ.