Клуб API Карт

Так как же всунуть в Яндекс карты тайлы приготовленые для Гугла, в том числе и карты самого гугла?

thekashey
14 декабря 2009, 10:16

На прошедшем субботнике товаришь из onboard.ru задался вопросом - как же ему отобразить на его Яндекс картах наложение terrain от гугла.

Давным давно я сам задавался этой проблемой когда вводил поддержку яндекс карт 

Докладчику из onboard я пообещал помочь.

Но что самое смешное - оказалочь что мой главный помощьник -   - сидел рядом со мной( спасибо ему за интересный топик, без которого труба)


Также благодарю создателя SasGis за уточнение математики. Уж кто-то, а он - первопроходец.


Итак - Google тайлы на яндекс картах - SOLVED

 

 

--разрыв страницы--?


Итак все мы знаем что координаты у яндекса и гугла совпадают, но перепутаны.

За подробностями - в топик вортекса.

Но если кратко - у яндекса другой сжатие по вертикали.

Ниже приведеный код позволяет отобразить тайлы гугла с учетом этого сжатия.

Все просто, и за разьяснениями в коменты наверное


для начала создадим свой ITile -

нам надо - получить тайл, и получить его вертикальный оффсет, относительно тайла яндекса


    function Yandex_myTile (indataSource) {
        var myPos = new YMaps.Point(),
        dataSource=indataSource,
        dy=0,
        element = document.createElement(window.XMLHttpRequest ? "IMG" : "DIV"),
        originalSize = new YMaps.Point(256);
        element.style.position='absolute';
        element.style.display='none';
   
        this.onAddToMap = function (map, parentContainer) {
            this.onRemoveFromMap();
            parentContainer.appendChild(element);
        }
   
        this.onRemoveFromMap = function () {
            if(element.parentNode)
             element.parentNode.removeChild(element);
            element.src='';
        }
           
        this.setPosition = function (position) {
            myPos.moveTo(position);
            element.style.left = myPos.x + 'px';           
            py=myPos.y+dy;// СДВИГАЕМ ТАЙЛ НА ПОЛУЧЕНЫЙ ОФСЕТ
            element.style.top = py + 'px';
        }
   
        this.getPosition = function () {
            var p = myPos.copy();
            return p;
        }
       
        this.pushTo=function(a)
        {
            var e=element;
            if (window.XMLHttpRequest)
            {
                e.style.display="none";
                e.onload =function(){this.style.display='block'};
                e.src=a;
            }
           
            else// будем уважать секратарш на IE6
            {
                e.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+a+"',sizingMethod='scale')";
                e.style.display="";
                e.src=a;
            } 
        }
   
        this.load = function (tile, zoom) {
            element.style.display='none';
            var src=dataSource.getTileUrl(tile,zoom);
            dy=dataSource.yoffset; // СМОТРИМ СЮДА! НАМ НУЖЕН ЭТОТ ОФСЕТ
            this.pushTo(src);
           
            this.setPosition(this.getPosition());
        }
   
        this.error = function () {
        }
       
        this.abort = function () {
        }
       
        this.scale = function (coeff) {
            var newSize = originalSize.copy().scale(coeff);
            element.style.width = newSize.x + 'px';
            element.style.height = newSize.y + 'px';
        }
    }   


Далее нам требуется создать наш яндексTileDataSource ( предложение к разрабочикам - почему бы GoogleTileSource не добавить в стандартный АПИ? )


var tileDataSource = new YMaps.TileDataSource(urlTemplate, true, false);
     
      tileDataSource.map=this.map;
      tileDataSource.getTile=function(){return new Yandex_myTile(this);}
      // первая мат функция
      tileDataSource.tileToMercator = function ( d ) {
        return {x:(Math.round(d.x / 53.5865938 - 20037508.342789)), y: (Math.round(20037508.342789 - d.y / 53.5865938))};
      }
     //вторая мат функция
     tileDataSource.mercatorToGeo = function(p) {
            // Предвычисленные коэффициенты согласно WGS84
           var ab = 0.00335655146887969400,
                bb = 0.00000657187271079536,
                cb = 0.00000001764564338702,
                db = 0.00000000005328478445;
                Rn = 6378137;
       
            var xphi = Math.PI/2 - 2 * Math.atan(1 / Math.exp(p.y/Rn));
       
            var latitude = xphi + ab * Math.sin(2 * xphi) + bb * Math.sin(4 * xphi) + cb * Math.sin(6 * xphi) + db * Math.sin(8 * xphi);
            var longitude = p.x/Rn;
       
            return new YMaps.GeoPoint(longitude * 180 / Math.PI, latitude * 180 / Math.PI, true);
        }
        //третья мат функция
        tileDataSource.fromLatLngToPixel = function(a, b)
        {
            var c = 256 * Math.pow(2, b);
            var Mw = c / 2;
            var Ow = c / 360;
            var Pw = c / (2 * Math.PI);
           
            var x = Math.round(Mw + a.getLng() * Ow);
            var y = Math.min(Math.max(Math.sin(a.getLat() / 180 * Math.PI), -0.9999), 0.9999);
            y = Mw + 0.5 * Math.log((1 + y) / (1 - y)) * (-Pw);
            y = Math.round(y);
           
            return new YMaps.Point(x, y);
        };

      //и само получение тайла, самое интересное
      tileDataSource.getTileUrl=function(tile,zoom)
      {
        var n = this.map.worker.api;  //(это инстанс яндекс карт )
       
        //преобразуем тайл в пикселы на 23(?) зуме
        var p=n.tileCoordinates.toPixels(tile,new YMaps.Point(0,0), zoom);
        //переводим пиксели в координаты гугла
        var geo = this.mercatorToGeo( this.tileToMercator(p));
        //переводим координаты в тайлпиксели гугла
        var geopt =  this.fromLatLngToPixel(geo, zoom);
        //преобразуем тайл-пиксели яндекса в текущий зим
        var yaopt={x:0,y:0};             
        yaopt.x=p.x>>(23-zoom);
        yaopt.y=p.y>>(23-zoom);
        //нам интересна разница!               
        this.yoffset=(yaopt.y-geopt.y);
       
        //в случае больших сдвигов - компенсируем
        var off=128;
        while(this.yoffset>off)
        {
            tile.y--;
            this.yoffset-=256;
        }
       
        while(this.yoffset<-off)
        {
            tile.y++;
            this.yoffset+=256;
        }
        return myparent.tileURL+"x="+tile.x+"&y="+tile.y+"&z="+zoom;
      }

      this.layer = new YMaps.Layer(tileDataSource);


Вот и все, боться больше нечего :)






14 комментариев
Подписаться на комментарии к посту
Под кат.
ужастный тут кат :(
А вы тот кто написал (и задал) больше всех вопросов в конце на доске с листами? Первые три написанных вопроса ваши? )

А можно алгоритм обратного преобразования?


Есть x,y,z по Google, нужно определить тайл Яндекса.

Границы тайлов яндекса не соответствуют границам тайлов гугла, потому только по xyz нельзя определить конкретный тайл яндекса. Вам для чего это нужно?
переводим xyz в гео координаты, переводим эти координаты на плоскость в пространстве яндекса.
делим на 256, получаем тайл.
остаток от деления - смещение тайла

формулу перевода xyz->google можно увидеть в любом месте инета( меркаторус обыкновенус ), google->xyz яндекса - лично я в первый раз увидел( с правильными коэфицентами ) в генераторе "активных тайлов"

прошу много, но могли бы Вы пояснить несколько моментов:

вот начиная отсюда:

//преобразуем тайл-пиксели яндекса в текущий зим

и далее, а конкретно:

- причём тут побитовые сдвиги? и что такое yaopt - yanadex o point?

yaopt.x=p.x>>(23-zoom);

- что за, логику работы не въеду, что и куда сдвигаем:

//в случае больших сдвигов - компенсируем

- что такое

return myparent.tileURL+"x="+tile.x+"&y="+tile.y+"&z="+zoom;

и почему не "return this.getTileUrlTemplate()....."

Вот и все, боться больше нечего :)

:)

пс.: неужели всем всё понятно?

1.яндекс выдает пиксельные координаты тайла на 23 зуме.

2.из них получаем геокоординаты "по гуглу"

3.переводим эти координаты обратно в пиксели(уже другие какбы)

4.p.x>>(23-zoom) - перевод "яндекс-тайл-координат" с 23 зума в заданный. кто не знает >>N это деление на 2^N

5.Там мы получили разницу между тайлами яндекса и гугла. Если она больше 256 пикселей - это уже не смешение, а просто изменение индекса в адресе тайла.

6. return myparent.tileURL+"x="+tile.x+"&y="+tile.y+"&z="+zoom;

на вкус и цвет фломастеры разные.

Спасибо, по коду 99% понятно, но...

//преобразуем тайл в пикселы на 23(?) зуме

что значит вопрос? 23 - это максимальный для Яндекса? вроде там 27 был..

И я интегрировал к себе и получаю такую цифру:

this.yoffset = yaopt.y - geopt.y;
this.yoffset= 2231

насколько я разобрался это цифра именно для 23 масштаба, но в коде она используется независимо от масштаба?

переформулирую:

при zoom=17, this.yoffset=26793px там, где у меня визуально смещение 30-50px

цифры (offset) и должны быть такими большими?

относительно чего это смещение?

пс: про макс. масштаб я ошибся - 23 это максимум.

как-то крутовато.

Если память мне не изменяет величина офсета от зума не зависит, только от Y, и не на москве около 512

можете ссылочку кинуть на самый простенький пример переключений карт гугла и яндекса, никак не получается сделать

Видимо так и не скинут((( У самого так ничего и не получилось((

Вот только не пойму как прикрутить это, что-то совсем у меня не раблтает.

Есть какой работающий пример?