Клуб API Карт

Измерение дистанции в декартовой системе

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

Добрый день! Наткнулась на неожиданную проблему.

У меня карта описывается декартовыми координатами

var myProjection = new ymaps.projection.Cartesian([
[-110, -110], // координаты левого нижнего угла
[110, 110] // координаты правого верхнего угла
    ]), 

 Есть квадратный объект (во всяком случае, на экране он отображается как квадратный).  Измеритель расстояния ("линейка") при измернии по горизонтали дает, к примеру, 200 м, а при измерении того же объекта по вертикали  - примерно в три раза больше.

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

5 комментариев
Алексей Yarrr!
28 января 2016, 04:03

Похоже, что проблема с проекцией, которую использует инструмент линейка.

Может в настройках где-то выставляется.

Ну, во всяком случае в документации я нигде не нашла намека, что "линейка" как-то настраивается, кроме как состояния Включена/Выключена.

Интересно еще и другое - в АПИ 1, судя по документации, можно было настаривать масштаб - т.е. сколько метров соотвествуют единице твоей координатной системы. В АПИ 2 я такой настройки не вижу, экспериментальным путем установлено, что одна единица - примерно 111  км (в документации к АПИ 1 эта цифра упоминалась, но ее можно было менять).

Кроме того, также экспериментальным путем вроде как обнаружилось, что чем меньше размер моей системы (8х8, против 220Х220 как у меня планируется), тем меньше разность в измерениях квадрата по горизонтали и вертикали. Но система 8х8 меня не устраивает, потому что при масштабе 0 у меня не влазит все, что должно бы влазить в мой "мир". А уже даже 100х100 заметно глючит при измерениях.

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

 

Алексей Yarrr!
28 января 2016, 04:03

Думаю, вам надо копать в эту сторону: http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/router.Path.xml

или в эту: http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/behavior.Ruler.xml

с помощью исходников: http://bit.ly/M1JlHv

 

Завязываться на 111км крайне не советую. Есть yourMap.options.get('projection'), с ней легко работать: Берете координаты (в пкс, или гео), и гоняете их пиксельными между гео координатами как надо: http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/projection.Cartesian.xml В вашем случае, гео - это локальные координаты на карте, в зависимости от текущего зума, оно будет (по крайней мере должно) само правильно их скейлить туда-обратно.

 

В исходниках нашел: var RulerBehavior

методы startListening и stopListening заигнорены, но вы можете на них завязаться, просто следите за ними сами. Если я не ошибаюсь, то их можно прокинуть через свойства (или опции?) как-то так: (.rules.options.set('startListening', function () { ... })). Еще из кода можно увидеть, как именно посылаются события. Поэтому, легко можно понять, куда надо их повешать, чтобы линейка стала работать, и примерно понятно, как она работает в штатном режиме.


Желаю удачи).

p.s. Делитесь ссылками на свой ресурс - так легче понять, что у вас за проблема и чем можно помочь)

 

Спасибо за ссылку на исходник - там много интересного почитать про декартову систему.

Путем экспериментов и неких умозаключений пришла к выводу, в чем возможная причина глюка - не знаю почему, но похоже, что линейка мои декартовы координаты по прежнему воспринимает как географические. Поскольку я экспериментировала на тайле 0,0 (вся карта у меня  пока не залита, вожусь с одним тайлом на разных зумах), то соответственно и измерения линейкой вела в районе координат 100, -100, что, похоже, было воспринято линейкой как "приполярная" область и была введена поправка на разность шага по широте и долготе. Когда я отъехала в зону "экватора", к координатам, близким к 0,0, то разность между измерениями по вертикали и горизонтали практически исчезла.

Ссылки на ресурс пока нет, поскольку на данный момент все существует на локалхосте, но карта у меня описывается вот такими параметрами (версию 2.0.10 пришлось выставить принудительно, потому что на версии 2.0 линейка стала неожиданно мерять в сантиметрах там, где вчера были сотни километров)

 

...

var myProjection = new ymaps.projection.Cartesian([
[-10, -10], // координаты левого нижнего угла
[10, 10] // координаты правого верхнего угла. идет сначала У, потом Х!!!!!!!
]), 

  MainMapLayer = function () {
return new ymaps.Layer(
     function (tile, zoom) {   
var tileurl="http://127.0.0.1/tiles/"+zoom+"/tile-" + tile[0] + "-" + tile[1] + ".jpg";
return tileurl;
  })};

  ymaps.layer.storage.add('my#world', MainMapLayer);
   ymaps.mapType.storage.add('my#world', new ymaps.MapType(
  'Карта',
['my#world']
));
    myMap = new ymaps.Map('map', {
center: [0, 0],
zoom: 0,
    type: 'my#world'
}, {
  maxZoom: 23, 
  minZoom: 0, 
projection: myProjection
});

разглядывая исходники, на которые Вы дали ссылку (2.0.10) , обнаружила интересный фрагмент

/**
     * @ignore
     * Возвращает расстояние между двумя заданными точками (в метрах), если двигаться
     * вдоль прямой соединяющей точки на карте. Используется для расчета расстояний
     * в элементе управления "Линейка".
     * @param {Number[]} point1 Первая точка.
     * @param {Number[]} point2 Вторая точка.
     * @param {Number} [radius=6378137] Радиус.
     * @returns {Number} Расстояние "по линейке" между двумя заданными точками.
     */     // todo kill
    rulerDistance: function (point1, point2, radius) {
        if (coordOrder == 'longlat') {
            point1 = swapCoords(point1);
            point2 = swapCoords(point2);
        }
        radius = radius || 6378137;
        var lat1 = point1[0],
            long1 = point1[1],
            lat2 = point2[0],
            long2 = point2[1],
            dist = 0;
        if (!(Math.abs(lat2 - lat1) < eps && Math.abs(long1 - long2) < eps)) {
            var latAV = ( lat1 + (lat2 - lat1) / 2 ) * Math.PI / 180,
                pathAngle = Math.atan( ((long2 * 60  - long1 * 60)/ ( lat2 * 60 - lat1 * 60)) * Math.cos(latAV) ),
                distPerDegree = 2 * Math.PI * radius / 360;
            dist = Math.abs(lat2 - lat1) < eps
                ? Math.abs(((long2 - long1) / Math.sin(pathAngle)) * Math.cos(latAV) * distPerDegree)
                : Math.abs(distPerDegree * (lat2 - lat1) / Math.cos(pathAngle));
        }
        return dist;
    }
};

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