Перекрывающиеся маркеры на карте становятся одной из первых практических проблем при работе с Google Maps JavaScript API, когда плотность объектов на ограниченной территории возрастает. При масштабировании карты несколько маркеров оказываются в одной и той же точке экрана или настолько близко, что пользователь не может различить их и взаимодействовать с каждым отдельно. Стандартные средства API решают задачу частично (кластеризация), но не покрывают сценарии, где требуется сохранить индивидуальные маркеры и обеспечить доступ ко всем объектам одновременно.
OverlappingMarkerSpiderfier представляет собой клиентскую библиотеку, предназначенную для «разворачивания» перекрывающихся маркеров в паукообразную структуру (spiderfy), позволяя пользователю увидеть и выбрать каждый объект по отдельности без потери контекста.
Основная идея заключается в динамическом изменении позиции маркеров, находящихся в одной географической точке или слишком близко друг к другу. Вместо наложения они временно распределяются по окружности вокруг исходной точки.
Ключевые этапы:
Важный момент: библиотека работает поверх существующих маркеров, не заменяя их и не изменяя данные слоя.
Библиотека не входит в стандартный набор API и подключается отдельно:
<script src="https://unpkg.com/overlapping-marker-spiderfier@1.0.3/lib/oms.min.js"></script>
При использовании модульной сборки:
npm install overlapping-marker-spiderfier
Далее импорт:
import OverlappingMarkerSpiderfier from 'overlapping-marker-spiderfier';
Работа начинается после создания экземпляра карты:
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 50.45, lng: 30.52 },
zoom: 10
});
Далее создаётся экземпляр OMS:
const oms = new OverlappingMarkerSpiderfier(map, {
markersWontMove: true,
markersWontHide: true,
basicFormatEvents: true
});
Эти флаги напрямую влияют на производительность при большом количестве объектов.
Каждый маркер должен быть зарегистрирован в системе spiderfy:
const marker = new google.maps.Marker({
position: { lat: 50.451, lng: 30.523 },
map: map
});
oms.addMarker(marker);
После добавления библиотека начинает отслеживать пересечения маркеров автоматически.
OMS расширяет стандартную модель событий маркеров, добавляя специфические состояния.
oms.addListener('click', function(marker) {
console.log('Маркер выбран:', marker);
});
oms.addListener('spiderfy', function(markers) {
console.log('Развёрнуто маркеров:', markers.length);
});
oms.addListener('unspiderfy', function(markers) {
console.log('Схлопывание обратно:', markers.length);
});
При активации spiderfy используется геометрическое распределение:
Упрощённая модель:
x = centerX + radius * cos(angle)
y = centerY + radius * sin(angle)
Такое распределение гарантирует отсутствие наложений даже при высокой плотности точек.
OMS не зависит от уровня zoom напрямую, но реагирует на изменение пиксельной плотности маркеров. При увеличении масштаба маркеры могут автоматически «схлопываться», если расстояние между ними становится достаточным.
Интеграция с событиями карты:
google.maps.event.addListener(map, 'zoom_changed', function() {
console.log('Изменён масштаб карты');
});
OverlappingMarkerSpiderfier и MarkerClusterer решают разные задачи и могут конфликтовать при неправильной архитектуре.
Типовой подход:
В таких системах OMS применяется на уровне конечных маркеров внутри кластера.
При работе с тысячами маркеров критичны следующие аспекты:
Не стоит добавлять все маркеры без необходимости:
if (shouldRender(point)) {
oms.addMarker(marker);
}
Повторное использование маркеров снижает нагрузку на GC.
OMS уже оптимизирует события, но дополнительные обработчики на каждом маркере увеличивают нагрузку.
OMS не ограничивает тип отображения маркера:
const marker = new google.maps.Marker({
position: { lat: 50.45, lng: 30.52 },
icon: {
url: '/custom-icon.png',
scaledSize: new google.maps.Size(32, 32)
}
});
При spiderfy сохраняются все визуальные свойства.
Несмотря на универсальность, существуют ограничения:
Типовые области применения:
В таких сценариях OMS выступает как слой взаимодействия между пользователем и геометрической плотностью данных.
OMS не перехватывает стандартные события карты, но расширяет модель взаимодействия с маркерами:
oms.addListener('click', function(marker, event) {
map.panTo(marker.getPosition());
});
Это позволяет строить кастомную логику навигации при выборе объекта.
Поведение системы можно формализовать как задачу:
Таким образом, библиотека реализует локальное преобразование пространства отображения без изменения исходных координат данных.