Как определить координаты XY(пиксельные) точки на плане? если известны координаты gps 4-ех его углов

Есть план картинка, у которого известны ширина, высота, а также координаты gps всех углов плана.

Как получить пиксельные координаты случайной точки на плане, если известны её gps координаты?

  • План на "карте" может находиться под любым углом.
  • Высота над уровнем моря не важна, кривизной земли тоже можно пренебречь.
  • Заранее известно что точка 100% лежит на плане, и не может лежать за его границами
  • План строго имеет прямоугольную форму

Go шаблон

package main

import (
    "fmt"
)

type Point struct {
    Num     uint    `json:"num" xml:"num" bson:"num" sql:"unique_index:idx_point_num"`
    X       float64 `json:"x" xml:"x" bson:"x"`
    Y       float64 `json:"y" xml:"y" bson:"y"`
    LatLng
}

type LatLng struct {
    Lat float64 `json:"lat" xml:"lat" bson:"lat"`
    Lon float64 `json:"lon" xml:"lon" bson:"lon"`
}

func main() {
    points := make([]Point, 4)
    points[0].Num = 1 //Левый Верхний
    points[0].X = 0.0
    points[0].Y = 0.0
    points[0].LatLng.Lat = 59.94082501317843
    points[0].LatLng.Lon = 30.313837582926084

    points[1].Num = 2 //Правый Верхний
    points[1].X = 1182.0
    points[1].Y = 0.0
    points[1].LatLng.Lat = 59.934845940050806
    points[1].LatLng.Lon = 30.36341272121419

    points[2].Num = 3 //Правый Нижний
    points[2].X = 1182.0
    points[2].Y = 174.0
    points[2].LatLng.Lat = 59.931189953842654
    points[2].LatLng.Lon = 30.361653262718022

    points[3].Num = 4 //Левый Нижний
    points[3].X = 0.0
    points[3].Y = 174.0
    points[3].LatLng.Lat = 59.937169032133106
    points[3].LatLng.Lon = 30.312080655319818
    
    targetPoint := LatLng{Lat:59.937169032133106, Lon:30.312080655319818}

    X := ???
    Y := ???

    fmt.Println(X)
    fmt.Println(Y)
}
points - массив точек (кол-во 4) описывающий координаты всех 4 углов прямоугольника/плана
targetPoint - gps координаты точки, пиксельные координаты которой требуется определить.

Ответы (1 шт):

Автор решения: Sergey

за неимением ответа решил через сопоставление прямоугольников, с последующим расчётом через треугольник. В пределах одного здания, и даже стадиона, точность конвертации координат очень велика. Проверил и в северном и в южном полушарии, и при разных углах поворота плана относительно карты мира.

    private static ImmutablePair<Integer,Integer> GetPixelFromGps(
        CalibrationPoints[] points, //массив из 4 точек, обозначающих углы плана
        GpsCoordinates gps, // координаты точки в gps
        double width, // ширина плана
        double height // высота плана
    ) {

        double w = distanceInKmBetweenEarthCoordinates( //расстояние от левого вернего угла до правого верхнего, он же width в километрах
            points[0].getLat(),
            points[0].getLon(),
            points[1].getLat(),
            points[1].getLon()
        );

        double h = distanceInKmBetweenEarthCoordinates( //расстояние от левого вернего угла до левого нижнего, он же height в километрах
            points[0].getLat(),
            points[0].getLon(),
            points[3].getLat(),
            points[3].getLon()
        );

        double x0 = distanceInKmBetweenEarthCoordinates( //расстояние от левого вернего угла до точки, в километрах
            points[0].getLat(),
            points[0].getLon(),
            gps.getLat(),
            gps.getLon()
        );

        double x1 = distanceInKmBetweenEarthCoordinates( //расстояние от правого вернего угла до точки, в километрах
            meta.getCalibrationPoints()[1].getLat(),
            meta.getCalibrationPoints()[1].getLon(),
            gps.getLat(),
            gps.getLon()
        );

        BigDecimal kefX = BigDecimal.valueOf(w).divide(BigDecimal.valueOf(meta.getWidth()), 20, RoundingMode.UP); // коеф. отношения ширины в километрах к пикселям
        BigDecimal kefY = BigDecimal.valueOf(h).divide(BigDecimal.valueOf(meta.getHeight()), 20, RoundingMode.UP); //коеф. отношения высоты в километрах к пикселям

        double pX = (w + x0 + x1) / 2; // полупериметр
        BigDecimal zX = BigDecimal.valueOf(Math.sqrt(pX * (pX - w) * (pX - x0) * (pX- x1))).multiply(BigDecimal.valueOf(2)).divide(new BigDecimal(w),20, RoundingMode.UP); // высота треугольника
        BigDecimal xtX = BigDecimal.valueOf(Math.sqrt(BigDecimal.valueOf( x0 * x0 ).subtract(zX.multiply(zX)).doubleValue())); // катет треугольника
        BigDecimal x = xtX.divide(kefX,20, RoundingMode.UP);
        BigDecimal y = zX.divide(kefY,20, RoundingMode.UP);
        return new ImmutablePair<Integer,Integer>(Math.round( x.doubleValue()), Math.round(y.doubleValue()) );
    }

    private static double distanceInKmBetweenEarthCoordinates(double lat1, double lon1, double lat2, double lon2) {
        double earthRadiusKm = 6371.0;
        double dLat = degreeToRadian(lat2-lat1);
        double dLon = degreeToRadian(lon2 - lon1);
        lat1 = degreeToRadian(lat1);
        lat2 = degreeToRadian(lat2);

        double a = Math.sin(dLat/2)*Math.sin(dLat/2) + Math.sin(dLon/2)*Math.sin(dLon/2)*Math.cos(lat1)*Math.cos(lat2);
        double c = 2*Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

        return earthRadiusKm * c;
    }

для наглядности

введите сюда описание изображения

→ Ссылка