如何将足球场上的位置转换为矩形上的坐标?

How to convert a location on football pitch to coordinates on rectangle?

我有4个足球场点(角点):

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

以及球场上的位置:

L(lat, lon, alt)

我想在大小为 (W, H) 的矩形上将 L(lat, lon, alt) 转换为 L(x, y)

如何实现这个转换功能? (我更喜欢 C# 语言,但实现语言并不重要)

下图描述了我的问题(我不知道如何实现Function框):

首先,因为输出坐标是二维的,我假设我们可以从输入坐标中去除高度信息。所以输入数据由定义输入矩形的四个点组成:

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

和输出矩形的尺寸:wh

我还要忽略地球的曲率(足球场够小)。有了这些假设,我们可以通过执行仿射变换来实现转换功能。每次我们想要执行转换时都创建转换矩阵会很浪费。出于这个原因,我们需要两个函数:第一个创建变换矩阵(只调用一次),第二个将使用该矩阵执行变换本身(可能调用多次,我们要变换的每个点一次) ,类似于:

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

请注意,我们只需要四个输入点中的三个即可在 2D 欧几里德中明确定义一个旋转矩形 space。

下面是 createTransformationMatrixtransform 函数的实现:

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();

我已经包含了所有的矩阵处理逻辑,因此这个代码片段是自包含的,但我建议您改用一些线性代数库来进行矩阵处理。

我也做了a more visual demo.