Клуб API Карт

получение данных для видимой области карты

Пост в архиве.

Здравствуйте, встретился с аналогичной задачей как обсуждалась тут (обсуждение)

поискал информацию про spatial indexes. Тема довольно темная, если честно, но вопрос не в том. Подскажите как реализовать подобную связку

карта + БД?

попробую расписать подробней: у меня в БД в разных столбцах хранятся координаты, мне нужно выбрать запросом только те, которые попадают в видимую на данный момент область карты, но для этого мне нужно получить в цифровом виде эту видимую область карты, ну например координаты верхнего левого угла и длину и ширину или координаты верхнего левого и нижнего правого угла, как это сделать средствами АПИ?

и вопрос номер 2: в приведенной выше ссылке тем кто обращается правильно написаны события реакции на зум и перемещение или есть другие варианты?

53 комментария
Sergey Konstantinov
28 января 2016, 01:54
Как правило, лучший вариант - хранить в базе координаты + номер тайла.
В таком случае на клиенте определяются номера тайлов, попадающих в область карты, и с сервера запрашиваются данные по этим тайлам. Работает на сотнях тысяч меток без всяких spatial indexes.
Номер тайла получить так:
map.getGlobalPixelCenter() - вернет координаты центра в пикселях от левого верхнего угла мира.
map.container.getSize() - размер контейнера карты. Соответственно, левый верхний угол = центр - полразмера, правый нижний = центр + полразмера.
Номер тайла получается из пиксельных координат делением на 256.

скажите, а возможен такой вариант:

1. находим центр карты функцией map.container.getSize() из нее определяем координаты л-верхнего угла и п-нижнего и получаем диапазон координат для выборки. для примера:

координаты л-в контейнера 23.5, 67.8

координаты п-н контейнера 25.6, 70

значит и БД нужно выбрать широты в диапозоне от 23,5 до 25,6 и долгоду от 67,8 до 70 или так не работает?

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

Sergey Konstantinov
28 января 2016, 01:54
> находим центр карты функцией map.container.getSize() из нее определяем координаты л-верхнего угла и п-нижнего и получаем диапазон координат для выборки.

Для этого есть готовая функция getBounds()

> значит и БД нужно выбрать широты в диапозоне от 23,5 до 25,6 и долгоду от 67,8 до 70 или так не работает?

Так работает, но (а) сдвиг даже на 1 пиксель требует перезапроса данных, откуда (б) данные плохо кэшируются в браузере. Тайловый вариант привязывает вас к сетке с шагом 256 пикселей.

> скажите я могу создать коллекцию геообъектов из координат заданныых тайлами?

В API глобальные пиксели пересчитываются в координаты и обратно с помощью map.options.get('projection').(to|from)GlobalPixels
На стороне сервера это можно сделать по формулам.

ок, я сформирую тайловые координаты для БД, итак у меня в базе есть координаты обычные и тайловые

пользователь кликает на кнопку "искать" дальше как я понял я определяю тайловые координаты л-в и п-н по формуле что вы мне написали и в запросе к БД я ограничиваю не фактические координаты, а тайловые? или как?

не могу уловить механизм действий, можно вас попросить  небольшой пример, алгоритм по пунктам и если не сложно с формулами.

как это вижу я:

1. клик "поиск"

2. определяем тайловый л-в и тайловый п-н

3. формируем json с с тайловыми координатами

4. делаем выборку из БД

поправьте, если в чем -то не прав 

Sergey Konstantinov
28 января 2016, 01:54
Да, вы все правильно пишете
На сервер уходит запрос типа &xmin=200&ymin=100&xmax=250&ymax=130, сервер выбирает все объекты, попадающие в этот диапазон и отдаёт в виде json.

спасибо за ответ, попробую реализовать

возник еще один вопрос, скажите, а зачем пиксельные координаты делить на 256

> Номер тайла получается из пиксельных координат делением на 256.

то есть я должен широту и долготу левого верхнего угла и правого нижнего угла разделить на 256?

сейчас конкретный пример, как у меня:

координаты центра относительно в-л угла мира 38286.40372053334,19114.524623120382

размер контейнера карты 813,470

левый верхний угол = 38286.40372053334 - 813,470/2 (широта) 19114.524623120382 - 813,470/2 (долгота)

правый нижний угол = 38286.40372053334 + 813,470/2 (широта) 19114.524623120382 + 813,470/2 (долгота)

чтобы определить номер тайла, я должен результаты еще поделить на 256?

Sergey Konstantinov
28 января 2016, 01:54
Пиксельные координаты делят на 256, потому что в одном тайле 256 пикселей :)

не, вопрос был не для чего делить на 256, а верны ли мои формулы?)))

левый верхний угол = 38286.40372053334 - 813,470/2 (широта) 19114.524623120382 - 813,470/2 (долгота)

правый нижний угол = 38286.40372053334 + 813,470/2 (широта) 19114.524623120382 + 813,470/2 (долгота)



а потом делаем так:

л-в широта/256

л-в долгота/256

п-н широта/256

пн долгота/256

так?:)

и нужно ли округлять результат?

Sergey Konstantinov
28 января 2016, 01:54
Верны ли формулы - зависит от того, какой у вас центр и масштаб у карты.
Округлять результат нужно (левую верхнюю границу вниз, правую нижнюю вверх), в этом вся соль метода :)
Вообще вот так должно быть:
левый верхний угол = [38286.40372053334 - 813/2, 19114.524623120382 - 470/2]
правый нижний = [38286.40372053334 + 813/2, 19114.524623120382 + 470/2]

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

 

Sergey Konstantinov
28 января 2016, 01:54
Ну, цифры на правду похожи.

ок, с этим вроде понял, дальше сам допилю, крайний вопрос:

вы дали формулу для преобразования географических координат в тайловые

1. Географические координаты в меркаторовы:
   var Rn = 6378137, // Экваториальный радиус
        e = 0.0818191908426, // Эксцентриситет
        esinLat = e * Math.sin(latitude);
function geoToMercator (longitude, latitude) {
    var tan_temp = Math.tan(Math.PI / 4.0 + latitude / 2.0),
         pow_temp = Math.pow(Math.tan(Math.PI / 4.0 + Math.asin(esinLat) / 2), e),
        U = tan_temp / pow_temp;

    return new Point(Rn * longitude, Rn * Math.log(U));
}
Широта и долгота задаются В РАДИАНАХ.

2. Преобразование меркаторовых координат в тайловые (пиксели на поседнем масштабе):
    var equatorLength = 40075016.685578488, //Длина экватора
         worldSize = Math.pow(2, 31), // Размер мира в пикселах
         a = worldSize / equatorLength,
         b = equatorLength /2;

function mercatorToPixels (p) {
    return new Point(
        Math.round((this._b + p.x) * this._a),
        Math.round((this._b - p.y) * this._a)
    );
}



их нужно преобразовывать в 2 этапа, сначала гео в меркаторовы, а потом меркаторовы в тайловые? за один раз преобразование не возможно?

Sergey Konstantinov
28 января 2016, 01:54
Можно, почему, слепите код в одну функцию.
Обратите внимание, что на сервере у вас получатся координаты на 23 масштабе (и это правильно), а с клиента будут приходить координаты согласно текущего масштаба карты z. Соответственно, входящие координаты с клиента надо будет привести к тому же масштабу (домножить на 2 в степени 23-z) и уже по ним искать в базе.

значит мне по сути не нужно передавать в get запросе масштаб карты, я передаю тайловые координаты умнаженные на 2 в степени 23?

значит итоговая формула для запроса выглядит так:

левый верхний угол = [(38286.40372053334 - 813/2)*2^23, (19114.524623120382 - 470/2)*2^23]

правый нижний = [(38286.40372053334 + 813/2, 19114.524623120382 + 470/2)*2^23]

и это будет универсальная формула и по ней можно искать диапозон тайлов в БД верно?


Sergey Konstantinov
28 января 2016, 01:54
Можно и так. (Тогда и в БД нужно хранить пиксели на 23 масштабе, а не тайлы.)
Только множитель не 23, а 23 минус текущий уровень масштабирования (8 в данном случае).
Но вообще информация о запрошенном масштабе на сервере пригодится, если в какой-то момент вы захотите фильтровать или кластеризовать точки.

Блин, загонная штука)

лучше скажу как у меня все сделано:

1. у меня в базе хранятся координаты объектов

2. у меня много фильтров и координаты после выборки собираются в кластеры

3. сейчас все работает по клику на кнопку поиск и не зависит от масштаба и выделенной области.

я понимаю, что я наверное один из самых деревянных людей с которыми вы работали, но я уже запутался:)

 

моя задача такова, что бы по кнопке поиск получалась первая выборка, а дальше карта реагировала на изменение масштаба и перемещении карты, что мне нужно добавить в БД?

и как к ней правильно обратиться?

как я вижу все:

1. координаты объектов остаются без изменения

2. добавляю координаты тайла в котором находится объект

3. определяю диапазон тайлов в активной области при конкретном масштабе и на конкретном участке карты

4. делаю выборку

 

как мне это можно сделать, что должен содержать запрос с клиентской части и как мне правильно сформировать тайлы в базе, что бы было максимально универсально и просто?

Sergey Konstantinov
28 января 2016, 01:54
Это зависит от того, сколько (какие порядки) у вас объектов будет попадать на карту.

результат выдачи всегда будет залимитирован на 200 записей.

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

как я понял тайл это квадрат карты размером 256*256 их для всего мира определенное количество и оно не меняется, потому что мир не растет и не уменьшается

понятное дело, что в тайл может попадать очень много точек, так почему нельзя абстрагироваться от масштаба карты, а просто определить координаты тайла верхнего левого угла активной области карты и нижнего правого и осужествлять выборку из этого диапазона, я не понимаю откуда и для чего берутся эти лишние данные вроде 2 в какой-то степени, 256 и прочеее, для чего все это, если тайл это по сути таже точка на карте, только размер у нее не 8*8 пикселей, а 256*256 пикселей или я упускаю какую-то фундаментальную особенность?

Sergey Konstantinov
28 января 2016, 01:54
Мир растёт и уменьшается, когда пользователь масштабирует карту. Размер мира равен 256*Math.pow(2, z), соответственно количество тайлов меняется от 1 на 0-м масштабе до 4 в степени z на масштабе z.

тогда как же все таки наилучшим образом хранить и выбирать данные?

если по вашему предложению хранить тайлы в БД при минимальном масштабе, значит для масштаба z=8 правильная выборка будет такая:

левый верхний угол = [(38286.40372053334 - 813/2)*2^(23-8), (19114.524623120382 - 470/2)*2^(23-8)]

правый нижний = [(38286.40372053334 + 813/2)*2^(23-8), 19114.524623120382 + 470/2)*2^(23-8)]

затем полученные результаты делим на 256 и округляем л-в в меньшую, п-н в большую сторону

и выбираем записи где

х1(широта)>л-в(широта) у1(долгота)>л-в(долгота) и

х2(широта)

верно?

 

Sergey Konstantinov
28 января 2016, 01:54
Да, если x, y - номера тайлов на 23-ем масштабе
И неравенства должны быть нестрогими (>=,

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

59.9214,30.3106---------------------------1073745035=>>>1073740200
60.2119,29.7016---------------------------1073745051=>>>1073740232
59.9851,30.2354---------------------------1073745038=>>>1073740204
59.7027,30.7884---------------------------1073745023=>>>1073740174
59.5563,31.0158---------------------------1073745015=>>>1073740162
59.6417,30.7910---------------------------1073745020=>>>1073740174
59.5462,30.8650---------------------------1073745015=>>>1073740170
60.0041,30.2818---------------------------1073745039=>>>1073740201
59.9412,30.3603---------------------------1073745036=>>>1073740197
60.0426,30.3287---------------------------1073745041=>>>1073740199
59.8904,30.4838---------------------------1073745033=>>>1073740190
60.0494,30.3051---------------------------1073745042=>>>1073740200
59.6417,30.7910---------------------------1073745020=>>>1073740174
59.7091,30.7812---------------------------1073745024=>>>1073740175
59.9926,30.6443---------------------------1073745039=>>>1073740182


слева координаты точки из БД, справа тайловые, получены вот по этой функции

function get_tail($x, $y){

$equatorLength = 40075016.685578488; //Длина экватора

$worldSize = POW(2, 31); // Размер мира в пикселах

$a = $worldSize / $equatorLength;

$b = $equatorLength /2;



echo round(($b + $x) * $a).'=>>>'.round(($b - $y) * $a)."
";

 

 

}


вопрос: почему размер мира $worldSize = POW(2, 31);  а не $worldSize = POW(2, 23);???

дальше попробовал найти тайловый диапазон

var world = myMap.getGlobalPixelCenter();

 

var container = myMap.container.getSize();

тайл верхнего правого угла - 4848628,2416580

найдено по формуле: 

((world[0] - container[0]/2) * Math.pow(2,15))/256+','+((world[1] - container[1]/2) * Math.pow(2,15))/256

тайл нижнего правого угла - 4952691,2476739

найдено по формуле: 

((world[0] + container[0]/2) * Math.pow(2,15))/256+','+((world[1] + container[1]/2) * Math.pow(2,15))/256

в общем все как-то не вяжется друг с другом, в чем может быть проблема и как свести результаты к верным?

Sergey Konstantinov
28 января 2016, 01:54
> получены вот по этой функции
Так не сработает, промежуточное преобразование в Меркаторовские координаты необходимо.

> почему размер мира $worldSize = POW(2, 31);  а не $worldSize = POW(2, 23);???

Потому что на 0-м масштабе размер мира 256 пикселей (28).

понял, буду делать преобразование в меркаторовские.

скажите, а вот эти значения: 

var world = myMap.getGlobalPixelCenter();

var container = myMap.container.getSize();

тайл верхнего правого угла - 4848628,2416580

найдено по формуле: 

((world[0] - container[0]/2) * Math.pow(2,15))/256+','+((world[1] - container[1]/2) * Math.pow(2,15))/256

тайл нижнего правого угла - 4952691,2476739

найдено по формуле: 

((world[0] + container[0]/2) * Math.pow(2,15))/256+','+((world[1] + container[1]/2) * Math.pow(2,15))/256


на правду похожи? есть какая нибудь функция, что бы их проверить?


Sergey Konstantinov
28 января 2016, 01:54
Похожи :)
Округление только добавьте.

Доброго времени суток, это снова я :)))

написал я функции преобразования, но снова ерунда получается... даже попробовал в js реализовать, то есть просто скопировать ваш текст и вот результат:

дано:

latitude = 30.3106

longitude = 59.9214

 

var Rn = 6378137, // Экваториальный радиус

        e = 0.0818191908426, // Эксцентриситет

        esinLat = e * Math.sin(latitude);

 

function geoToMercator (longitude, latitude) {

    var tan_temp = Math.tan(Math.PI / 4.0 + latitude / 2.0),

         pow_temp = Math.pow(Math.tan(Math.PI / 4.0 + Math.asin(esinLat) / 2), e),

        U = tan_temp / pow_temp;

 

    alert((Rn * longitude)+'********'+(Rn * Math.log(U)));

}

 

РЕЗУЛЬТАТ:

382186898.4318, -9171981.575783033

загружаю результат в другую функцию, для получения тайловых координат:

var equatorLength = 40075016.685578488, //Длина экватора

         worldSize = Math.pow(2, 31), // Размер мира в пикселах

         a = worldSize / equatorLength,

         b = equatorLength /2;

 

function mercatorToPixels (latitude, longitude) {

    alert(Math.round((this._b + longitude) * this._a)+'********'+Math.round((this._b - latitude) * this._a));

   

}

 

mercatorToPixels(-9171981.575783033, 382186898.4318);

 

РЕЗУЛЬТАТ: NaN, NaN

пробовал менять местами входные данные:

mercatorToPixels(382186898.4318, -9171981.575783033);

РЕЗУЛЬТАТ: NaN, NaN


Что я делаю не так? или мне просто пора рукава к джинсам пришивать?:))))

 

Sergey Konstantinov
28 января 2016, 01:54
Во-первых, вы забыли перевести градусы в радианы.
Во-вторых, во второй функции надо заменить this._a на a, а this._b на b.

ааа.... вот в чем дело.. теперь понятно, вечером попробую!) Спасибо за терпение и ответы!

до вечера недотерпел, попробовал! результат получил, пожалуйста посмотрите похож он на правду или нет? я соединил 2 функции в одну и на вход подаю и вывожу в порядке широта и долгота, а не долгота и широта как в вашем примере

latitude = 30.3106 * Math.PI/180;

longitude = 59.9214 * Math.PI/180;

 

function geoToMercator (latitude, longitude) {

 

var Rn = 6378137, // Экваториальный радиус

e = 0.0818191908426, // Эксцентриситет

esinLat = e * Math.sin(latitude);

 

var tan_temp = Math.tan(Math.PI / 4 + latitude / 2),

pow_temp = Math.pow(Math.tan(Math.PI / 4 + Math.asin(esinLat) / 2), e),

U = tan_temp / pow_temp;

 

lat = Rn * Math.log(U);

lon = (Rn * longitude);

 

var equatorLength = 40075016.685578488, //Длина экватора

worldSize = Math.pow(2, 31), // Размер мира в пикселах

a = worldSize / equatorLength,

b = equatorLength /2;

 

alert(Math.round((b - lat) * a)+'********'+Math.round((b + lon) * a));

 

}

geoToMercator (latitude, longitude);

 

это была функция с демоданными, а теперь результат:

широта: 885011118

долгота: 1431186898

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

 

я просто ранее в сообщениях приводил примеры вычисленных координат л-в и п-н углов активной области карты и там координаты тайлов получились обсолютно другие, даже порядок отличался на 2-3 знака...=(

Sergey Konstantinov
28 января 2016, 01:54
Результат правильный. Если его поделить на 256 с округлениям, то получатся номера тайлов на 23 масштабе.

ок, я поделал результаты на 256 и получил

широта: 3457075

долгота: 5590574

вроде результаты не плохие, но если сравнить с полученными ранее координатами видимой области видно несоответствие:

 

 

Масштаб карты - 8

тайл верхнего правого угла - 4848628,2416580

найдено по формуле: 

((world[0] - container[0]/2) * Math.pow(2,15))/256+','+((world[1] - container[1]/2) * Math.pow(2,15))/256

тайл нижнего правого угла - 4952691,2476739

найдено по формуле: 

((world[0] + container[0]/2) * Math.pow(2,15))/256+','+((world[1] + container[1]/2) * Math.pow(2,15))/256


даже если прикинуть, что широта и долгота меняеются местами, все равно результаты не вяжутся... все цифры семизначные, но диапазоны иксов (4848628, 4952691), а диапозон игриков (2416580, 2476739)

значения 

широта: 3457075

долгота: 5590574

в них явно не попадают, хотя точка с такими координатами находится в СП и она видна при 8-м масштабе карты

Sergey Konstantinov
28 января 2016, 01:54
Всё правильно. Теперь домножим на разность масштабов Math.pow(23 - 8), и все станет нормально.

так я и до этого домножал

((world[0] - container[0]/2) * Math.pow(2,15))/256+','+((world[1] - container[1]/2) * Math.pow(2,15))/256

((world[0] + container[0]/2) * Math.pow(2,15))/256+','+((world[1] + container[1]/2) * Math.pow(2,15))/256

или нужно еще раз?

Sergey Konstantinov
28 января 2016, 01:54
Так, я что-то запутался.

Берём все ту же точку (30.3106, 59.9214) на 8-ом масштабе
Считаем на клиенте:
var pixels = map.getGlobalPixelCenter(),
      size = map.container.getSize(),
      topLeft = [
           Math.floor((pixels[0] - size[0] / 2) * Math.pow(2, 23 - 8) / 256),
           Math.floor((pixels[1] - size[1] / 2) * Math.pow(2, 23 - 8) / 256)
      ]

Получаем тайловые координаты 5564973, 3437874 (для моей карты размером 200х300).
Вроде пасьянс сходится.
Обратите внимание: пиксельные координаты ВСЕГДА в порядке (x, y), независимо от того, в каком порядке координаты географические.

у меня все получилось, правда полного счастья так и не ощутил:) а все дело в том, что при малых масштабах карты 10 и меньше, карта работает просто отлично, но при большем приближении, например когда масштаб равен 12, он выдает результат , не для всей активной области, а только для нижней половины активной области, причем строго половина, если прибвать масштаб до 13, до уже на четверть, при блошем приближении результаты вовсе выходят и области видимости, причем диапазон тайлов он вроде как верно считает...

 

var world = myMap.getGlobalPixelCenter();

var container = myMap.container.getSize();

topLeft_X = Math.floor((world[0] - container[0] / 2) * Math.pow(2, 23 - myMap.getZoom()) / 256);

topLeft_Y = Math.floor((world[1] - container[1] / 2) * Math.pow(2, 23 - myMap.getZoom()) / 256);

bottLeft_X = Math.ceil((world[0] + container[0] / 2) * Math.pow(2, 23 - myMap.getZoom()) / 256);

bottLeft_Y = Math.ceil((world[1] + container[1] / 2) * Math.pow(2, 23 - myMap.getZoom()) / 256);

 

вот так определял тайлы для сервера:

function get_tail ($latitude, $longitude) {

$latitude = $latitude * M_PI/180;

$longitude = $longitude * M_PI/180;

 

$Rn = 6378137; // Экваториальный радиус

$e = 0.0818191908426; // Эксцентриситет

$esinLat = $e * sin($latitude);

 

$tan_temp = tan(M_PI / 4 + $latitude / 2);

$pow_temp = pow(tan(M_PI / 4 + asin(esinLat) / 2), $e);

$U = $tan_temp / $pow_temp;

 

$lat = $Rn * log($U);

$lon = ($Rn * $longitude);

$equatorLength = 40075016.685578488; //Длина экватора

$worldSize = pow(2, 31); // Размер мира в пикселах

$a = $worldSize / $equatorLength;

$b = $equatorLength /2;

$n_tailX = round((($b + $lon) * $a)/256);

$n_tailY=round((($b - $lat) * $a)/256);

}

 

и вот кусочек заброса на выборку:

WHERE  

`n_tailX`>=".$_GET['topLeft_X']." AND

`n_tailY`>=".$_GET['topLeft_Y']." AND

`n_tailX`

`n_tailY`

 

вроде все норм и все правильно, но стоит приблизить и получается ерунда...

Sergey Konstantinov
28 января 2016, 01:54
Это очень странно :)
Вы же пересчитываете диапазон тайлов при каждом изменении масштаба, да?

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

я ща посмотрел и есть четкая зависимость:

масштаб 8 - все нормально

масштаб 9 - результаты на 3/4 активной области, 1/4 сверху пуста

масштаб 10 - 2/4 активно области, 2/4 сверху пусто

и т.д. может формула не универсальна для всех масштабов?

 

Sergey Konstantinov
28 января 2016, 01:54
Надо пересчитывать на каждый boundschange :)
map.events.add('boundschange', function () {
// пошел пересчет
});

сделал пересчет, точно такая же фигня

масштаб 8 - все нормально

масштаб 9 - результаты на 3/4 активной области, 1/4 сверху пуста

масштаб 10 - 2/4 активно области, 2/4 сверху пусто

и т.д. может формула не универсальна для всех масштабов?

Sergey Konstantinov
28 января 2016, 01:54
Это очень странно.
Можете показать страницу?

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

готово, вот страничка

http://test-lev.fullbn.ru/all_object_on_map.html

реагирует на событие:

myMap.events.add('boundschange', function () {
// пошел пересчет
});

Sergey Konstantinov
28 января 2016, 01:54
Так, нашел одну проблему: вместо
topLeft_X = Math.floor((world[0] - container[0] / 2) * Math.pow(2, 23 - myMap.getZoom()) / 256);
Нужно писать
topLeft_X = Math.floor((world[0] - container[0] / 2)  / 256) * Math.pow(2, 23 - myMap.getZoom());
(аналогично 3 остальные формулы)
т.е. сначала привязываем к сетке 256х256, потом масштабируем до 23 масштаба.

это очень грустно... но проблема не решена... я изменил формулы в соответствии с правками, но точки упорно скользят вниз при увеличении масштаба...

может нужно вводить какой-то поправочный коэффициент начиная с конкретного уровня масштабирования?

 

P.S. я на хосте обновил с новыми формулами, можете убедиться, что при увеличении происходит смещение

Sergey Konstantinov
28 января 2016, 01:54
На сервер диапазоны тайлов уходят правильно:
topLeft_X=4866048&topLeft_Y=2424832&bottLeft_X=4931584&bottLeft_Y=2473984 - 9 масштаб
topLeft_X=4894720&topLeft_Y=2441216&bottLeft_X=4911104&bottLeft_Y=2453504 - 11 масштаб
topLeft_X=4899840&topLeft_Y=2445312&bottLeft_X=4903936&bottLeft_Y=2448384 - 13 масштаб
Все цифры соответствуют реальной видимой области.
Выходит, проблема где-то на серверной стороне. Формулы вроде бы похожи на правду, единственное, что не
$n_tailX = round((($b + $lon) * $a)/256);
$n_tailY=round((($b - $lat) * $a)/256);
а floor, но это никакого существенного эффекта не должно давать.
Попробуйте сбросить колонку с номерами тайлов в базе и пересчитать все тайловые координаты по новой.
И покажите ваш код выборки, пожалуйста.

попробовал сделать то, что вы рекомендуете, floor на самом деле счастья в дом не принес, а что касается кодов, то вот:

запрос на выборку из БД:

SELECT *

FROM  `o_flats` 

WHERE

`n_tailX`>=".$_GET['topLeft_X']." AND

`n_tailY`>=".$_GET['topLeft_Y']." AND

`n_tailX`

`n_tailY`

ORDER BY  `n_count_rooms` ASC


а вот полный код преобразования координат в тайлы, мб поможет:

$result1 = mysql_query("

SELECT *

FROM  `o_flats` 

WHERE 1"

);

while($row = mysql_fetch_object($result1)){

get_tail($row->s_latitude, $row->s_longitude, $row->id);

}

function get_tail ($latitude, $longitude, $id) {

$latitude = $latitude * M_PI/180;

$longitude = $longitude * M_PI/180;

$Rn = 6378137; // Экваториальный радиус

$e = 0.0818191908426; // Эксцентриситет

$esinLat = $e * sin($latitude);

$tan_temp = tan(M_PI / 4 + $latitude / 2);

$pow_temp = pow(tan(M_PI / 4 + asin(esinLat) / 2), $e);

$U = $tan_temp / $pow_temp;

$lat = $Rn * log($U);

$lon = ($Rn * $longitude);

$equatorLength = 40075016.685578488; //Длина экватора

$worldSize = pow(2, 31); // Размер мира в пикселах

$a = $worldSize / $equatorLength;

$b = $equatorLength /2;

mysql_query("UPDATE  `o_flats` SET  `n_tailX` =  '".floor((($b + $lon) * $a)/256)."',

`n_tailY` =  '".floor((($b - $lat) * $a)/256)."' WHERE  `o_flats`.`id` =".$id." LIMIT 1");

 

}


вот и все волшебство!:)

Sergey Konstantinov
28 января 2016, 01:54
А дайте пару примеров - какие были координаты у объекта, какой получился номер тайла.

координаты: широта - 59.9214 долгота - 30.3106--------------------------->>> тайл: n_tailX - 4900592 n_tailY - 2439707
координаты: широта - 60.2119 долгота - 29.7016--------------------------->>> тайл: n_tailX - 4886401 n_tailY - 2426141
координаты: широта - 59.9851 долгота - 30.2354--------------------------->>> тайл: n_tailX - 4898839 n_tailY - 2436742
координаты: широта - 59.7027 долгота - 30.7884--------------------------->>> тайл: n_tailX - 4911725 n_tailY - 2449842
координаты: широта - 59.5563 долгота - 31.0158--------------------------->>> тайл: n_tailX - 4917024 n_tailY - 2456589
координаты: широта - 59.6417 долгота - 30.7910--------------------------->>> тайл: n_tailX - 4911786 n_tailY - 2452657
координаты: широта - 59.5462 долгота - 30.8650--------------------------->>> тайл: n_tailX - 4913510 n_tailY - 2457053
координаты: широта - 60.0041 долгота - 30.2818--------------------------->>> тайл: n_tailX - 4899921 n_tailY - 2435857
координаты: широта - 59.9412 долгота - 30.3603--------------------------->>> тайл: n_tailX - 4901750 n_tailY - 2438786
координаты: широта - 60.0426 долгота - 30.3287--------------------------->>> тайл: n_tailX - 4901013 n_tailY - 2434062
координаты: широта - 59.8904 долгота - 30.4838--------------------------->>> тайл: n_tailX - 4904628 n_tailY - 2441148
координаты: широта - 60.0494 долгота - 30.3051--------------------------->>> тайл: n_tailX - 4900464 n_tailY - 2433744
координаты: широта - 59.6417 долгота - 30.7910--------------------------->>> тайл: n_tailX - 4911786 n_tailY - 2452657
координаты: широта - 59.7091 долгота - 30.7812--------------------------->>> тайл: n_tailX - 4911557 n_tailY - 2449546
координаты: широта - 59.9926 долгота - 30.6443--------------------------->>> тайл: n_tailX - 4908367 n_tailY - 2436393
координаты: широта - 59.9941 долгота - 30.6433--------------------------->>> тайл: n_tailX - 4908344 n_tailY - 2436323
координаты: широта - 59.5462 долгота - 30.8650--------------------------->>> тайл: n_tailX - 4913510 n_tailY - 2457053
координаты: широта - 59.9930 долгота - 30.6417--------------------------->>> тайл: n_tailX - 4908307 n_tailY - 2436374
координаты: широта - 60.1439 долгота - 30.2247--------------------------->>> тайл: n_tailX - 4898590 n_tailY - 2429327
координаты: широта - 60.2537 долгота - 29.6077--------------------------->>> тайл: n_tailX - 4884213 n_tailY - 2424179
координаты: широта - 59.5362 долгота - 30.8772--------------------------->>> тайл: n_tailX - 4913794 n_tailY - 2457513
координаты: широта - 59.6215 долгота - 30.3868--------------------------->>> тайл: n_tailX - 4902367 n_tailY - 2453588
координаты: широта - 59.5386 долгота - 30.3819--------------------------->>> тайл: n_tailX - 4902253 n_tailY - 2457403
координаты: широта - 59.5462 долгота - 30.8650--------------------------->>> тайл: n_tailX - 4913510 n_tailY - 2457053
координаты: широта - 59.5735 долгота - 30.1403--------------------------->>> тайл: n_tailX - 4896623 n_tailY - 2455798
координаты: широта - 59.8837 долгота - 30.9977--------------------------->>> тайл: n_tailX - 4916602 n_tailY - 2441459
координаты: широта - 59.9935 долгота - 30.6422--------------------------->>> тайл: n_tailX - 4908319 n_tailY - 2436351
координаты: широта - 59.9371 долгота - 31.0280--------------------------->>> тайл: n_tailX - 4917308 n_tailY - 2438977
координаты: широта - 59.7049 долгота - 30.7908--------------------------->>> тайл: n_tailX - 4911781 n_tailY - 2449740
координаты: широта - 59.7020 долгота - 30.7884--------------------------->>> тайл: n_tailX - 4911725 n_tailY - 2449874
координаты: широта - 59.7027 долгота - 30.7884--------------------------->>> тайл: n_tailX - 4911725 n_tailY - 2449842
координаты: широта - 59.7027 долгота - 30.7884--------------------------->>> тайл: n_tailX - 4911725 n_tailY - 2449842
координаты: широта - 59.9906 долгота - 30.6450--------------------------->>> тайл: n_tailX - 4908384 n_tailY - 2436486
координаты: широта - 59.7027 долгота - 30.7884--------------------------->>> тайл: n_tailX - 4911725 n_tailY - 2449842
координаты: широта - 59.6440 долгота - 30.8555--------------------------->>> тайл: n_tailX - 4913289 n_tailY - 2452551
координаты: широта - 59.4265 долгота - 29.6724--------------------------->>> тайл: n_tailX - 4885721 n_tailY - 2462547
координаты: широта - 59.4877 долгота - 29.3626--------------------------->>> тайл: n_tailX - 4878502 n_tailY - 2459740
координаты: широта - 59.3502 долгота - 31.2509--------------------------->>> тайл: n_tailX - 4922502 n_tailY - 2466038
координаты: широта - 59.4642 долгота - 29.6278--------------------------->>> тайл: n_tailX - 4884681 n_tailY - 2460819
координаты: широта - 59.6022 долгота - 29.6704--------------------------->>> тайл: n_tailX - 4885674 n_tailY - 2454477
координаты: широта - 60.3670 долгота - 28.6162--------------------------->>> тайл: n_tailX - 4861109 n_tailY - 2418849
координаты: широта - 60.3942 долгота - 29.5622--------------------------->>> тайл: n_tailX - 4883153 n_tailY - 2417567
координаты: широта - 59.4451 долгота - 32.0266--------------------------->>> тайл: n_tailX - 4940577 n_tailY - 2461694
координаты: широта - 59.4161 долгота - 31.1260--------------------------->>> тайл: n_tailX - 4919592 n_tailY - 2463023
координаты: широта - 60.5244 долгота - 28.7867--------------------------->>> тайл: n_tailX - 4865082 n_tailY - 2411413
координаты: широта - 59.5581 долгота - 30.1325--------------------------->>> тайл: n_tailX - 4896442 n_tailY - 2456506
координаты: широта - 59.4222 долгота - 29.0424--------------------------->>> тайл: n_tailX - 4871040 n_tailY - 2462744


столько достаточно? я могу полный список дать

Sergey Konstantinov
28 января 2016, 01:54
Достаточно. По Y получается неправильно:
59.9214, 30.3106 должно переводиться в 4900592, 2447454

Проверяем:
широта в радианах = 1.0458257224045302
esinLat = 0.070801310169654
tan_temp = 3.7218374812254993
pow_temp = 1.0058194825143463
U = 3.7003036289590003
Меркаторовская долгота: 8345249.345768923
Номер тайла: 2447454

Проверяйте, где-то должно разойтись :)

да, вы правы, меня подвела невнимательность, забыл знак $ в строке

$pow_temp = pow(tan(M_PI / 4 + asin($esinLat) / 2), $e);

исправил, вот результат:

координаты: широта - 59.9214 долгота - 30.3106--------------------------->>> тайл: n_tailX - 4900592 n_tailY - 2447454

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

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

или может тут дело в кластерах ( хотя это вообще уже сюрреализм:) )

оууу! прошу прощения, мой косяк! почему то в функции не срабатывает запрос на обновление данных в БД, сейчас проверю и отпишусь, возможно, что все уже работает нормально!

УРАААА!:) 

Все работает, оказывается на хосте для БД было ограничение на функцию UPDATE, я ограничение убрал, обновил тайлы и все заработало! Всем огромное спасибо за внимание к моей проблеме, выкладываю готовую функцию для перевода координат в тайлы (может кому понадобится):

 

function get_tail ($latitude, $longitude) {

$latitude = $latitude * M_PI/180; //перевод в радианы

$longitude = $longitude * M_PI/180; //перевод в радианы

 

$Rn = 6378137; // Экваториальный радиус

$e = 0.0818191908426; // Эксцентриситет

$esinLat = $e * sin($latitude);

 

$tan_temp = tan(M_PI / 4 + $latitude / 2);

$pow_temp = pow(tan(M_PI / 4 + asin($esinLat) / 2), $e);

$U = $tan_temp / $pow_temp;


$merkat_lat = $Rn * log($U);//меркаторовские координаты

$merkat_lon = ($Rn * $longitude);//меркаторовские координаты

$equatorLength = 40075016.685578488; //Длина экватора

$worldSize = pow(2, 31); // Размер мира в пикселах

$a = $worldSize / $equatorLength;

$b = $equatorLength /2;

 

$tail_X = floor((($b + $merkat_lon) * $a)/256);//номер тайла

$tail_Y = floor((($b - $merkat_lat) * $a)/256);//номер тайла

}



на стороне клиента определение границ видимой области:

var world = myMap.getGlobalPixelCenter(); //точка в центре контейнера относительно верхнего левого угла мира

var container = myMap.container.getSize(); //размер контейнера с картой


topLeft_X = Math.floor((world[0] - container[0] / 2) / 256) * Math.pow(2, 23 - myMap.getZoom()); //левый верхний угол координата Х

topLeft_Y = Math.floor((world[1] - container[1] / 2) / 256) * Math.pow(2, 23 - myMap.getZoom()); //левый верхний угол координата Y

bottLeft_X = Math.ceil((world[0] + container[0] / 2) / 256) * Math.pow(2, 23 - myMap.getZoom());//правый низний угол координата X

bottLeft_Y = Math.ceil((world[1] + container[1] / 2) / 256) * Math.pow(2, 23 - myMap.getZoom());//правый низний угол координата Y


и будет счастье! Всем спасибо и удачи в разработках!!!