Работа с координатами в Google Maps JavaScript API часто требует вычисления центра между несколькими географическими точками. Под центром может пониматься как простое среднее значение широты и долготы, так и геодезически корректная точка на поверхности сферы Земли. Выбор метода зависит от масштаба карты, распределения точек и требуемой точности отображения.
Самый прямолинейный способ вычисления центра между двумя точками — усреднение их широт и долгот:
[ lat_{center} = , lng_{center} = ]
Этот подход корректен только при небольших расстояниях, когда искажения проекции минимальны.
function getMidpoint(lat1, lng1, lat2, lng2) {
return {
lat: (lat1 + lat2) / 2,
lng: (lng1 + lng2) / 2
};
}
Использование в Google Maps JavaScript API:
const pointA = { lat: 55.7558, lng: 37.6173 };
const pointB = { lat: 59.9311, lng: 30.3609 };
const center = getMidpoint(pointA.lat, pointA.lng, pointB.lat, pointB.lng);
const map = new google.maps.Map(document.getElementById("map"), {
center: center,
zoom: 6
});
При работе с глобальными координатами возникают существенные ошибки:
Для повышения точности применяется сферическая модель.
Более корректный способ основан на переводе географических координат в трёхмерные декартовы координаты на единичной сфере.
Пусть широта и долгота переводятся в радианы:
[ lat = , lng = ]
Тогда:
[ x = ()(), y = ()(), z = ()]
После суммирования всех точек вычисляется среднее значение координат:
[ x_{avg} = , y_{avg} = , z_{avg} = ]
Затем результат нормализуется обратно на сферу и переводится в широту и долготу:
[ = (y, x), = (z, )]
function getGeographicCenter(points) {
let x = 0, y = 0, z = 0;
points.forEach(p => {
const lat = p.lat * Math.PI / 180;
const lng = p.lng * Math.PI / 180;
x += Math.cos(lat) * Math.cos(lng);
y += Math.cos(lat) * Math.sin(lng);
z += Math.sin(lat);
});
const total = points.length;
x /= total;
y /= total;
z /= total;
const lng = Math.atan2(y, x);
const hyp = Math.sqrt(x * x + y * y);
const lat = Math.atan2(z, hyp);
return {
lat: lat * 180 / Math.PI,
lng: lng * 180 / Math.PI
};
}
В контексте карты часто требуется вычислить центр группы маркеров:
const markers = [
{ lat: 55.7558, lng: 37.6173 },
{ lat: 59.9311, lng: 30.3609 },
{ lat: 56.8389, lng: 60.6057 }
];
const center = getGeographicCenter(markers);
const map = new google.maps.Map(document.getElementById("map"), {
center,
zoom: 5
});
Практически в Google Maps JavaScript API часто используется подход через границы области отображения. Он не требует сферических вычислений и хорошо подходит для визуального центрирования.
[ lat_{center} = , lng_{center} = ]
function getBoundsCenter(points) {
let minLat = Infinity;
let maxLat = -Infinity;
let minLng = Infinity;
let maxLng = -Infinity;
points.forEach(p => {
if (p.lat < minLat) minLat = p.lat;
if (p.lat > maxLat) maxLat = p.lat;
if (p.lng < minLng) minLng = p.lng;
if (p.lng > maxLng) maxLng = p.lng;
});
return {
lat: (minLat + maxLat) / 2,
lng: (minLng + maxLng) / 2
};
}
Google Maps API предоставляет встроенный класс для работы с границами:
const bounds = new google.maps.LatLngBounds();
points.forEach(p => {
bounds.extend(new google.maps.LatLng(p.lat, p.lng));
});
const center = bounds.getCenter();
const map = new google.maps.Map(document.getElementById("map"), {
center: center.toJSON(),
zoom: 5
});
Арифметический метод:
Сферический метод:
Bounding box:
При обновлении набора маркеров центр часто пересчитывается автоматически:
function updateMapCenter(map, points) {
const center = getGeographicCenter(points);
map.setCenter(center);
}
Такой подход позволяет адаптировать карту под изменяющийся набор данных без ручного управления масштабом и позицией.
При наличии точек по разные стороны 180-го меридиана стандартные методы bounding box дают ошибку, расширяя область на всю карту. В таких случаях используется нормализация долгот:
В некоторых сценариях каждая точка имеет значимость:
function getWeightedCenter(points) {
let x = 0, y = 0, z = 0, weightSum = 0;
points.forEach(p => {
const lat = p.lat * Math.PI / 180;
const lng = p.lng * Math.PI / 180;
const w = p.weight || 1;
x += Math.cos(lat) * Math.cos(lng) * w;
y += Math.cos(lat) * Math.sin(lng) * w;
z += Math.sin(lat) * w;
weightSum += w;
});
x /= weightSum;
y /= weightSum;
z /= weightSum;
const lng = Math.atan2(y, x);
const hyp = Math.sqrt(x * x + y * y);
const lat = Math.atan2(z, hyp);
return {
lat: lat * 180 / Math.PI,
lng: lng * 180 / Math.PI
};
}