Как стать автором
Обновить

Как подружить Yandex карты с Google и OSM?

Время на прочтение 7 мин
Количество просмотров 24K


Есть у меня проект, который работает на основе карт от Яндекса. Выбрал я их потому, что там документация на русском, хорошее качество карт наших городов и приятный интерфейс. Позже, как оказалось, еще и большие возможности.
И вот я заметил, что некоторые места Яндекс спутник показывает в слишком маленьком масштабе, что не годиться для построения маршрутов.
Изначально, я сделал два отдельных файла со скриптами, в одном из которых – была логика работы с Яндексом, а во втором – с Google. Переключение происходило в профиле и действовало на весь сайт, а вся работа с картами на сайте была в псевдокоде(прокси).
Esosedi.ru использовали как раз такой подход с переключением карт, но без перезагрузки. Однако такой вариант мне не подходил потому, что используются маркеры и ломанные линии, к тому же вести два разных файла трудно.

И вот я решил поискать другой вариант.

Вариант 1. Загрузка тайлов


Себе приписывать чужого не буду. Этот вариант я нашел в клубе яндекс карт clubs.ya.ru/mapsapi/replies.xml?item_no=7125. Переделав всю логику под этот вариант и, добавив уже OSM, я было обрадовался, что все готово и уже думал о другом, но тут с ужасом увидел, что карты отображаются неправильно. Координаты совпадают, но где-то часть карт не подгружаются, появляются серые места, то сверху, то снизу. Проблема была в том, что не только широта с долготой перепутаны, а используются принципиально разные системы координат. При пересчете на гугловские координаты надо было сдвинуть тайлы по вертикали на определенную величину. Вот как раз эта величина и была серой областью.
В интернете писали что такой вариант не лечиться. Я пробовал увеличить карту по вертикали сверху и снизу, но скрыть ее. После я уже увидел что не только серые полоски появляются сверху, а прям на карте, линия квадратов может не подгружаться. Однако оставил я этот вариант не потому.
Google разрешает использовать свои тайлы только через API, а значит придется в любом случае создавать карту, но вот как отображать метки одной карты на другой?

Вариант 2. Копирование тайлов


План был таков.
Создается экземпляр карты Google где-то далеко-далеко, а точнее, либо с отрицательным индексом, либо с отрицательной величиной top или left. После, навешивается обработчик событий на карту Yandex, который при движении карты Yandex двигает карту Google. У карты Google стоит обработчик движение, при котором копируются divы с картами в div Яндекс карт с классом «YMaps-tile-container», в котором находятся тайлы Яндекса.
GEvent.addListener(gmap,"move", function (overlay, latlng) {
var point = gmap.getCenter();
$layer = $('#googlemap div img').parent().parent().parent();
$('.GMaps-tile-container').html($layer.html()).width($layer.width()).height($layer.height());
$('.GMaps-tile-container').css('left',$('.YMaps-tile-container').css('left'))
.css('top',$('.YMaps-tile-container').css('top'))
.css('width',$('.YMaps-tile-container').css('width'))
.css('height',$('.YMaps-tile-container').css('height'));
$('.YMaps-tile-container').hide();
}, this);
GEvent.addListener(gmap,"tilesloaded", function (overlay, latlng) {
var point = gmap.getCenter();
$layer = $('#googlemap div img').parent().parent().parent();
$('.GMaps-tile-container').html($layer.html()).width($layer.width()).height($layer.height());
$('.GMaps-tile-container').css('left',$('.YMaps-tile-container').css('left'))
.css('top',$('.YMaps-tile-container').css('top'))
.css('width',$('.YMaps-tile-container').css('width'))
.css('height',$('.YMaps-tile-container').css('height'));

}, this);

* This source code was highlighted with Source Code Highlighter.

При небольшом размере карты все работает довольно неплохо, но стоит открыть на весь экран, как тут начинается полный абзац.

Вариант 3. Двигать карту


Следующий вариант, который пришел на ум, это было просто двигать карту, которая находилась бы внутри другой карты.
Сама карта гугл создается в диве с классом «YMaps-tile-container». Див с тайлами Яндекса скрывается, а точнее не скрывается, а получает прозрачность 0, ведь именно этот див обрабатывает все движения карты.
Добавляем обработчик событий к карте Яндекса. При движении карты Yandex будет происходить синхронизация центра карты Яндекса с картой Google.
YMaps.Events.observe(map, map.Events.Move, function (oMap, offset) {
    gmap.setCenter(new GLatLng(map.getCenter().getLat(), map.getCenter().getLng()), map.getZoom());    
  });
YMaps.Events.observe(map, map.Events.BoundsChange, function () {
  gmap.setCenter(new GLatLng(map.getCenter().getLat(), map.getCenter().getLng()), map.getZoom());
  });


* This source code was highlighted with Source Code Highlighter.

Пробуем… И опять, при небольшом размере карты все работает хорошо, но при разворачивании начинаются заметное торможение и неприятные артефакты.
Возможно это связано с тем, что параллельно грузятся карты Яндекса, хоть их и не видно. Создадим пустой тип карты и установим его.
var none = new YMaps.TileDataSource('', false, false);
var tnone = function () {return new YMaps.Layer(none)};
YMaps.Layers.add("none#layer", tnone);
map.setType(new YMaps.MapType(["none#layer"],'',{minZoom:1, maxZoom:17}));


* This source code was highlighted with Source Code Highlighter.

Опять пробуем и опять тормозит. Опытным путем я выяснил, что основное торможение происходит из-за того, что у Яндекса, при перемещении карты, происходит плавное торможение. После долгих поисков, я не нашел как его отключить, а значит такой вариант не подходит.

Вариант 3.1. Использование moveBy


Вот на этой странице api.yandex.ru/maps/jsapi/examples/synhromaps.html находится пример, как синхронизировать две Яндекс карты между собой. Вместо центрирования там используется сдвиг на определенный интервал(видимо такой вариант снижает нагрузку). У карт Google есть точно такая же функция, но значения сдвига Яндекс карты не подходит. После нескольких опытов я оставил и эту затею.

Вариант 4. Двигать маркеры


Если вариант с движением тайлов не прокатил из-за большой нагрузки, то вариант с движением объектов должен быть лучше.
План схож с вариантом 3, за одним исключением: обработкой событий должен заниматься обработчик Google карт. Для этого надо скрыть слои Яндекса, отвечающий за обработку событий.
И вот тут нас ждет разочарование. По непонятным причинам Гугл карты в данном варианте обрабатывают только нажатие кнопки мыши, но не передвижение и отпускание. И сколько часов я потратил на поиск результатов не сосчитать.
После долгих экспериментов в попытках удалить стандартные обработчики Яндекс Карт, оказалось, что надо сделать чуть-чуть по-другому и все заработает.
Для начала надо перенести Гугла карты из дива «YMaps-tile-container» в самый корень, то есть в div, в котором создается сама Яндекс карта. Теперь карту покрывает серый фон. Установим значение background дива с классом «YMaps-layer-container» в transparent. Теперь мы видим и карту и объекты, но гугл не получает событий. Установим z-index гугл карт в 1, а у дива «YMaps-layer-container» поставим auto. Пробуем… Готово!

И волки сыты и овцы целы!

Все уместилось в две функции, код которых можно увидеть ниже.
Тут же можно подключить и OSM.
//Вызывается при смене карты.
function unbindGoogleMap()
{
  $('#googlemap').hide();
  $('.YMaps-map-type-layer-container').show();
  $('.YMaps-layer YMaps-common-object-layer').show();
  if (mapevents[0])
  mapevents[0].cleanup();
}
/Вызывается при выборе Google карты
function bindGoogleMap(type)
{
  var selector = '#'+$(map.getContainer()).attr('id');
  map.setType(new YMaps.MapType(["none#layer"],'',{minZoom:1, maxZoom:17}));
  map.redraw();
  if ($('#googlemap').length==0)
  {
    var $div = $('<div>').attr('id','googlemap').width($(selector).width()).height($(selector).height()).css({zIndex:1});
    $('#routemap').prepend($div);
  }
  $('#googlemap').show();
  $('.YMaps-layer-container').css({background:'transparent',zIndex:'auto'});
  $('.YMaps-map-type-layer-container').hide();
  $('.YMaps-layer YMaps-common-object-layer').hide();
  if (!gmap)
  {
    gmap = new GMap2(document.getElementById('googlemap'));
    gmap.enableContinuousZoom();
    gmap.enableScrollWheelZoom();
  }
  switch(type)
  {
    case 'gotsat': case 4:
    gmap.setMapType(G_SATELLITE_MAP);;
    break;
    case 'gothyb': case 5:
    gmap.setMapType(G_HYBRID_MAP);;
    break;
    case 'gotrel': case 6:
    gmap.setMapType(G_PHYSICAL_MAP);;
    break;
    default: gmap.setMapType(G_NORMAL_MAP);
  }  
  gmap.setCenter(new GLatLng(map.getCenter().getLat(), map.getCenter().getLng()), map.getZoom());
  //Двигают картой
  GEvent.addListener(gmap,"move", function (overlay, latlng) {
    map.setCenter(new YMaps.GeoPoint(gmap.getCenter().lng(), gmap.getCenter().lat()), gmap.getZoom());
  }, this);
  //Как-то изменяют область видимости
  GEvent.addListener(gmap,"update", function (overlay, latlng) {
    map.setCenter(new YMaps.GeoPoint(gmap.getCenter().lng(), gmap.getCenter().lat()), gmap.getZoom());
  }, this);  
  //Если нажимают на контроллер зума
  mapevents[0] = YMaps.Events.observe(map, map.Events.BoundsChange, function () {
    gmap.setCenter(new GLatLng(map.getCenter().getLat(), map.getCenter().getLng()), map.getZoom());
  });
}


* This source code was highlighted with Source Code Highlighter.



На этом все. Живой пример можно увидеть на сайте bikecamp.ru/map
Теги:
Хабы:
+64
Комментарии 31
Комментарии Комментарии 31

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн