Pull to refresh

Я разрежу тебя на тайлы. Часть первая

Reading time 5 min
Views 11K
image Несмотря на все мои попытки к исправление ситуации с технологиями загрузки данных на карту, люди все продолжают и продолжают работать по старинке, загружая либо все данные вьюпорта при малейшем движении карты, либо просто загружая все данные вообще.
Покуда метры направления растеризует маркеры в картинки, строят супер системы группировок и кластеризаций и дрожат над каждым лишним килобайтом в скриптах, давайте я расскажу самый простой, стабильны, и банальный способ сделать жизнь свою, своих пользователей, своих карт и своего сервера чуть прекрасней.
Как будет сказано чуть позже покорение карт состоит из 5ти шагов.
Первый из них это загрузка карты( не все так просто как думается ), а вот второй — это загрузка на эти самые карты данных.
Он то нам и интересен.

В настоящий момент распространены два подхода к загрузке данный маркеров на карту.
1. Грузим все данные через ajax, либо сразу их javascript
2. Дергаем сервер на предмет «а какие объекты у нас в текущем viewport?»
Первый вариант имеет право на жизнь, особенно если маркеров мало, да и markermanager тут помогает(скрывать невидимые), а вот второй давайте забудем как страшный сон.

Итак, будем считать

что у нас есть функция для загрузке данных по координатному боксу
  1. $.get("ajax/load-map-data?SE="+MySECorner.toString()+"&NE="+MyNECorner.toString()+"&zoom="+currentZoom,
  2.    function(data){
  3.     //wow! i got the data for map!
  4.     //for whole visible viewport( and just for it)
  5.    });
* This source code was highlighted with Source Code Highlighter.

Используя ее мы убиваем свой сервер невозможностью кеширования, а пользователя задержками при малейшем сдвиге карты( фи 4 aroundme номер раз ).

Как же этот хренов гугл работает так быстро!

Он вроде бы загружает картинки своих карт… мелкими кусочками, 256х256 пикселей.

Подумай %username%, почему бы не сделать тоже самое?


Если бы мы грузили свои данные таким образом то:
  • можно было бы просто обкешироваться на сервере
  • перемещение карты меньше чем на 256 пикселей, скорее всего, не приводил бы к подгрузке новых блоков данных
  • А так как нормальный браузер поддерживает 4-6 параллельных запросов к одному серверу( или даже больше, use jsonp luke! на пару субдоменов ), то и тоже самое количество данных мы получали бы в пару потоков и, в итоге, малек быстрее


Лично себя я отношу к категории извращенцев, и моя реализация таких вот хитрых запросов, плюс marker manager & clustering занимает порядка 6000 строк.
Не буду вас сейчас мучать тем о чем расскажу в последующих статьях.

А просто расскажу

Как сделать это просто


Реализация представлена для google maps v3, она при желании за секунды переделывается под yandex карты( берем тайлы под гугл и отображаем их на яндекс картах, либо реализуем описанный интерфейс изначально на яндекс ITile и используем их ковертер), либо, чуть поработав напильником, под google maps v2
В любом случае есть замечательный SparseTileLayer разобрав который можно отвязаться как от апи карт, так и от всего остального( и который мы сейчас используем, так как на момент окончания разработки карт пользовательских вариантов в v3 не было)


Итак создадим свой тип карты
  1. function ServerFetchMapType() {
  2. }
  3.  
  4. ServerFetchMapType.prototype.tileSize = new google.maps.Size(256,256);
  5. ServerFetchMapType.prototype.maxZoom = 32;
  6. ServerFetchMapType.prototype.name = "Server Tile #s";
  7. ServerFetchMapType.prototype.alt = "Server Data Tile Map Type";   
  8.  
  9.  
  10. ServerFetchMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
  11.  var addr=this.getAddr(coord,zoom);
  12.  //test - may be we allready load this block?
  13.  if(ServerFetchedTiles[addr]){
  14.   return
  15.  }
  16.  
  17.  $.get("ajax/load-map-tile?"+addr,
  18.    function(data){
  19.     ServerFetchedTiles[addr]=data;
  20.     //wow! i got the data just for this tile
  21.    });
  22.  
  23. };
  24.  
  25.  
  26. //and add this tile overlay to current map type
  27. map.overlayMapTypes.insertAt(0, new ServerFetchMapType() );
* This source code was highlighted with Source Code Highlighter.

Как не странно — это все :)
Мы создали свой тип карты, только заместо картинок выполняем запросы к серверу.
А запросы бывают двух вариантов — гугла подобные, когда мы адресуем тайл по его x,y,z либо стандартные, по баундинг боксу.
Реализация данных методов:
  1. //we can request tile as tile(x,y,z)
  2. ServerFetchMapType.prototype.getAddrXY = function(coord, zoom){
  3.   return "x="+coord.x+"&y="+coor.y+"&z="+zoom;
  4. }
  5.  
  6. //or as latlng box
  7. ServerFetchMapType.prototype.getAddrLatLng = function(coord, zoom){
  8.   
  9.   //helper function for mercator projection
  10.   var mercator = function(point){
  11.    //
  12.    var numtiles=Math.pow(2,point.zoom);
  13.    var bitmapsize=numtiles*256;
  14.    var bitmaporigo    =bitmapsize/2;
  15.    var pixelsperlondegree=bitmapsize/360;
  16.    var pixelsperlonradian=bitmapsize/(2*Math.PI);
  17.   
  18.    var lat=bitmaporigo+(pixelsperlondegree*point.x);
  19.    var ged2rad=((point.t*Math.PI)/180.0);
  20.    var sn=Math.sin(deg2rad);
  21.    var lng=((bitmaporigo - 0.5 * log((1+sn)/(1-sn)) * pixelsperlonradian));
  22.    return new google.maps.LatLng(lat,lng);
  23.   }
  24.   var point1={x:coord.x*256,y:coord.y*256,z:zoom} ;
  25.   var point2={x:(coord.x+1)*256,y:(coord.y+1)*256,z:zoom} ;
  26.   var ne=mercator(point1);
  27.   var sw=mercator(point2);
  28.   return "NE="+ne.toString()+"&SW="+sw.toString()+"&z="+zoom;
  29. }
* This source code was highlighted with Source Code Highlighter.

* в данном случаем мы очень не красиво реализовали свою функцию меркатора, намного лучше использотьвать встроеные конвертеры(yandex.converter) и проекторы(overlay.projection) АПИ карт.

Что остается


Остается ответить на вопрос — что будет если у вас нету данные для загружаемых тайлов( вы находитесь посреди уральских гор ) и зачем вам в этом случае вообще грузить данные.

Ответ на этот вопрос я давал еще полтора года назад.
Чтож под конец сравним как работает подгрузка данных на wikimapia, esosedi, gdeetotdom и (стыд и позор програмистам!) aroundme

кстати следует уточнить что три указаных «умных» сайта используют кодировку «z-тайлов», по сути путь в quadtree, но викимапия, через свой апи, позволяет запрашивать данные в x-z-y адресации.

Вот и все


Я надеюсь что мои мысли дойдут до людей в этих мыслях нуждающихся, если будут вопросы — спрашивайте.
Если будут просто вопросы по картографии — тоже спрашивайте, раз уж мне пришлось начать свой блог( зеркало blogspot) на своем корявом английском, всегда буду рад написать о проблемах волнующих массы.

Что ж, пойду я дописывать вторую статью в которой расскажу много новых секретов, ранее на хабре не озвученных. А Вы запасайтесь кофем и болтайте
Tags:
Hubs:
+26
Comments 11
Comments Comments 11

Articles