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

Posted

技术标签:

【中文标题】如何将足球场上的位置转换为矩形上的坐标?【英文标题】:How to convert a location on football pitch to coordinates on rectangle? 【发布时间】:2020-02-17 15:50:31 【问题描述】:

我有 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框):

【问题讨论】:

航拍图和这个问题有什么关系?你所描述的似乎是一个简单的直线变换——线性代数。 @Prune 航拍照片显示Geographic coordinate system有4+1个点。 什么程序化输入来自照片。您将问题描述为具有 (lat, long, alt) 坐标,这似乎使照片完全无法处理。高度无关紧要,你有一个简单的线性变换......不是吗? @Prune 航拍图只是描述了问题,转换函数的签名应该是这样的:void Function(double p1lat, double p1lon, double p1alt, double p2lat, double p2lon, double p2alt, double p3lat, double p3lon, double p3alt, double p4lat, double p4lon, double p4alt, double Llat, double Llon, double Lalt, int width, int height, out int x, out int y) (1) 为什么海拔是输入的一部分? (2) 为什么这是一长串double 而不是一对点(对角将定义间距)? (3)目标W, H维度从哪里来? (4)你在哪里卡住了线性变换? 【参考方案1】:

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

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 欧几里得空间中明确定义旋转矩形。

这里是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();

我已经包含了所有的矩阵处理逻辑,所以这段代码 sn-p 是自包含的,但我建议你改用一些线性代数库来进行矩阵处理。

我也做了a more visual demo

【讨论】:

如果您想了解有关线性代数的更多信息,请务必查看 YouTube 上的 "Essence of linear algebra" by 3Blue1Brown。 This video 与这个问题的上下文特别相关。

以上是关于如何将足球场上的位置转换为矩形上的坐标?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 GPS 坐标转换为位置

将纬度和经度转换为图像上的坐标

HTML5 Canvas 中旋转矩形内的鼠标位置

R - 如何将纬度和经度坐标转换为地址/人类可读的位置?

如何获取表单上的光标位置?

自定义地图中的相对坐标计算