Как преобразовать местоположение на футбольном поле в координаты на прямоугольнике? - PullRequest
4 голосов
/ 21 октября 2019

У меня есть 4 точки футбольного поля (угловые точки):

P1(lat, lon, alt), P2(lat, lon, alt), P3(lat, lon, alt), P4(lat, lon, alt).

и местоположение на поле:

L(lat, lon, alt)

Я хочу преобразовать L(lat, lon, alt) в L(x, y) в прямоугольнике размером (W, H).

Как реализовать эту функцию преобразования? (Я предпочел язык C #, но язык реализации не важен)

Следующее изображение описывает мою проблему (я не знаю, как реализовать поле Function):

enter image description here

1 Ответ

2 голосов
/ 28 октября 2019

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

P1(lat, lon), P2(lat, lon), P3(lat, lon), P4(lat, lon)

и размеры выходного прямоугольника: w,h.

Я также собираюсь игнорировать кривизну Земли (футбольное поле достаточно маленькое). С этими допущениями мы можем реализовать функцию преобразования, выполнив аффинное преобразование. Было бы расточительно создавать матрицу преобразования каждый раз, когда мы хотим выполнить преобразование. По этой причине нам нужны две функции: первая для создания матрицы преобразования (вызывается только один раз) и вторая, которая будет использовать эту матрицу для выполнения самого преобразования (возможно, вызывается много раз, по одному разу для каждой точки, которую мы хотим преобразовать), что-то вроде:

tm = createTransformationMatrix(P1, P2, P4, w, h)
inPoint = (200, 50)
outPoint = transform(inPoint, tm)

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

Вот реализация createTransformationMatrix и transform функций:

const run = function() {

  // Creates transformation matrix to transform
  // from rectangle somewhere in 2D space with coordinates p0, px, pi, py
  // to rectangle with coordinates (x=0, y=0), (x=w, y=0), (x=w, y=h), (x=0, y=h).
  // Note that: p0 is mapped to (x=0, y=0)
  //            px is mapped to (x=w, y=0)
  //            py is mapped to (x=0, y=h)
  const createTransformationMatrix = function(p0, px, py, w, h) {
    // Translate px and py by p0 - pxt and pyt are px and py vectors in coordinate system in which p0 is at the origin
    const pxt = {
      x: px.x - p0.x,
      y: px.y - p0.y,
    };
    const pyt = {
      x: py.x - p0.x,
      y: py.y - p0.y,
    };

    // Create transformation matrix, which is inverse of transformation matrix that:
    //   1. Transforms (x=0, y=0) to (x=p0.x,             y=p0.y)
    //   2. Transforms (x=1, y=0) to (x=p0.x + pxt.x / w, y=p0.y + pxt.y / w)
    //   3. Transforms (x=0, y=1) to (x=p0.x + pyt.x / h, y=p0.y + pyt.y / h)
    return Matrix.invert3([
      [pxt.x / w, pyt.x / h, p0.x],
      [pxt.y / w, pyt.y / h, p0.y],
      [0        , 0        , 1   ],
    ]);
  };

  const transform = function(point, transformationMatrix) {
    // Convert point to homogeneous coordinates
    const inputVector = [
      [point.x],
      [point.y],
      [1],
    ];
    // Transform inputVector
    const outputVector = Matrix.multiply(transformationMatrix, inputVector);
    // Convert outputVector back to cartesian coordinates and return
    return {
      x: outputVector[0][0] / outputVector[2][0],
      y: outputVector[1][0] / outputVector[2][0],
    };
  };


  const w = 220;
  const h = 115;
  const p1 = {x:-79, y:80 };
  const p2 = {x:9,   y:-96};
  const p3 = {x:55,  y:-72};
  const p4 = {x:-34, y:105};

  const tm = createTransformationMatrix(p1, p2, p4, w, h);
  const inPoint  = {x: 200, y: 50};
  const outPoint = transform(inPoint, tm);
  console.log(`(${inPoint.x}, ${inPoint.y}) --[transform]--> (${outPoint.x}, ${outPoint.y})`);
}


//// Matrix ////
const Matrix = {};

Matrix.scale = (s, m) => m.map(x => Array.isArray(x) ? Matrix.scale(s, x) : s * x);

Matrix.multiply = function(a, b) {
  const aNumRows = a.length, aNumCols = a[0].length;
  const bNumRows = b.length, bNumCols = b[0].length;
  const m = new Array(aNumRows);
  for (let r = 0; r < aNumRows; ++r) {
    m[r] = new Array(bNumCols);
    for (let c = 0; c < bNumCols; ++c) {
      m[r][c] = 0;
      for (let i = 0; i < aNumCols; ++i)
        m[r][c] += a[r][i] * b[i][c];
    }
  }
  return m;
};

Matrix.invert3 = function(m) {
  const [[a, b, c],
         [d, e, f],
         [g, h, i]] = m;
  const det = a*(e*i - f*h) - b*(d*i - f*g) + c*(d*h - e*g);
  return Matrix.scale(1/det, [
    [e*i - f*h, c*h - b*i, b*f - c*e],
    [f*g - d*i, a*i - c*g, c*d - a*f],
    [d*h - e*g, b*g - a*h, a*e - b*d],
  ]);
};
//////////////

run();

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

Я также сделал более наглядной демонстрацией .

...