busidoway
@busidoway

Как расположить изображение на карте по точкам?

День добрый. Завис над одной задачей.

Допустим, на странице загружаем какое-либо изображение. На изображении указываем две точки A и B. Координаты точек указываются в пикселях: левое поле ввода - отступ сверху (координата y), правое поле ввода - отступ слева (координата x).

5e37e611a05f0682355980.png

Далее на карте указываем две точки, по которым необходим разместить изображение. Карты использовались OpenStreetMap, выводятся через библиотеку LeafLet.

5e37f20d98ccc099624639.png

Здесь координаты указываются в градусах.

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

5e38123486a87805440733.png

Пока что добился такого варианта.

Для размещения изображения использовал самописную формулу:

Ax - x точки А на изображении; Ay - y точки А на изображении
Bx - x точки B на изображении; By - y точки А на изображении
Agx - x точки А на карте (долгота); Agy - y точки А на карте (широта)
Bgx - x точки B на карте (долгота); Bgy - y точки B на карте (широта)
W - ширина изображения; H - высота изображения

Находим дельту точек A и B по x (на изображении):
ABX = Ax - Bx
Находим дельту точек A и B по y (на изображении):
ABY = Ay - By

Находим дельту точек A и B по x (на карте):
ABGX = Agx - Bgx
Находим дельту точек A и B по y (на карте):
ABGY = Agy - Bgy

Находим координаты точек:
GX1 = (0 - Ax) * ABGX / ABX + Agx
GX2 = GX3 = (W - Ax) * ABGX / ABX + Agx
GY1 = GY2 = (0 - Ay) * ABGY / ABY + Agy
GY3 = (H - Ay) * ABGY / ABY + Agy

GX3 и GY3 - дополнительные координаты. В принципе, их можно не учитывать. Нужны для разворачивания изображения на карте.

Вот, собственно, примерный код:

//отображение изображения по координатам из файла data_file_map.json (в файле содержатся данные координат)
$.ajax({
    url: '/uploads/data_file_map.json',
    dataType: 'json',
    success: function (data) {

        //координаты, по которым выстраивается изображение
        var bounds = [[data.map_lat1, data.map_lng1], [data.map_lat2, data.map_lng2], [data.map_lat3, data.map_lng3]];
        //создание слоя для изображения
        var image_layer = L.imageOverlay('/uploads/'+data.fileName, bounds, {opacity:0.4});
        //добавление слоя на карте
        image_layer.addTo(map);

    }
});

//получение данных изображения из data_image_param.json
$.ajax({
    url: '/uploads/data_image_param.json',
    dataType: 'json',
    success: function (data) {

        //координаты точек на карте
        var lat1 = $('#lat1').val(),
            lat2 = $('#lat2').val(),
            lat3 = $('#lat3').val(),
            lng1 = $('#lng1').val(),
            lng2 = $('#lng2').val(),
            lng3 = $('#lng3').val();
        
        //данные изображения
        var x1 = 0,
            y1 = 0,
            px1 = data.lng1, //x точки A
            py1 = data.lat1, //y точки A
            px2 = data.lng2, //x точки B
            py2 = data.lat2, //y точки B
            img_width = data.imgWidth, //ширина изображения
            img_height = data.imgHeight; //высота изображения
            
        var dpx = parseInt(px1) - parseInt(px2); //x точки А минус x точки B (на изображении)
        var dpy = parseInt(py1) - parseInt(py2); //y точки А минус y точки B (на изображении)
        var dpgx = lng1 - lng2; //долгота точки A минус долгота точки B (на карте)
        var dpgy = lat1 - lat2; //широта точки A минус широта точки B (на карте)
        
        //находим координаты для точек
        var gx1 = (0          - px1) * dpgx / dpx + parseFloat(lng1);
        var gx3 = (img_width  - px1) * dpgx / dpx + parseFloat(lng1);
        var gy1 = (0          - py1) * dpgy / dpy + parseFloat(lat1);
        var gy3 = (img_height - py1) * dpgy / dpy + parseFloat(lat1);
        var gx2 = gx3;
        var gy2 = gy1;

        //gx3 и gy3 нужны для корректного отображения изображения на карте (так требует leaflet, иначе изображение отобржается не так как надо)

        //полученные данные (gx1, gx2, gx3, gy1, gy2, gy3) сохраняются, допустим, в файл data_file_map.json, который используется для отображения изображения на карте

        /*

        .....
        
        */
                
    }
});


По итогу, изображение должно размещаться точно по точкам с сохранением своих пропорций и, если необходимо, с углом наклона. Как здесь:

5e38149a10b59655425068.png

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

P.S.

Для нахождения угла попробовал следующий вариант.

Загрузил изображение. Изображение растянулось по точкам. Нашел длину отрезков между точками A и B и на изображении и на карте. Определил процент разности между отрезками. При помощи этого процента задал ширину для изображения. Получилось изображение с сохраненными пропорциями.
Далее, чтобы рассчитать угол поворота определил "воображаемый" треугольник между точкам A, B (изображение) и B (карта).

5e3819374f867219140256.png

Написал функцию, чтобы рассчитать арккосинус, наподобие этой:

//арккосинус A
function getAcosA(a, b, c) {
					
	var arccos, angle;
	arccos = (a*a + c*c - b*b)/(2*a*c);
	angle = Math.acos(arccos);
	return (angle*180)/Math.PI;
					
}


Угол рассчитывает, но довольно не точный.
  • Вопрос задан
  • 254 просмотра
Решения вопроса 2
hint000
@hint000
у админа три руки
Без растягивания посчитайте на картинке угол alpha вектора AB относительно OY (или OX).
Потом посчитайте аналогично на карте угол beta. Вычтите один угол из другого - получите искомый угол поворота.
А коэффициент сжатия\растяжения - просто отношение длин отрезков AB на картинке и на карте (эх, ну что вы их одинаковыми буквами обозначили? хоть бы в одном месте маленькие буквы, в другом большие, а то неудобно же...)

P.S. В общем случае нужно учитывать картографическую проекцию, но это на порядок усложнит задачу.
Ответ написан
busidoway
@busidoway Автор вопроса
Кому интересно, рабочий вариант https://jsfiddle.net/busidoway/L26zqutd/189/
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы