如何将足球场上的位置转换为矩形上的坐标?
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)
和输出矩形的尺寸:w
、h
。
我还要忽略地球的曲率(足球场够小)。有了这些假设,我们可以通过执行仿射变换来实现转换功能。每次我们想要执行转换时都创建转换矩阵会很浪费。出于这个原因,我们需要两个函数:第一个创建变换矩阵(只调用一次),第二个将使用该矩阵执行变换本身(可能调用多次,我们要变换的每个点一次) ,类似于:
tm = createTransformationMatrix(P1, P2, P4, w, h)
inPoint = (200, 50)
outPoint = transform(inPoint, tm)
请注意,我们只需要四个输入点中的三个即可在 2D 欧几里德中明确定义一个旋转矩形 space。
下面是 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();
我已经包含了所有的矩阵处理逻辑,因此这个代码片段是自包含的,但我建议您改用一些线性代数库来进行矩阵处理。
我也做了a more visual demo.
我有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)
和输出矩形的尺寸:w
、h
。
我还要忽略地球的曲率(足球场够小)。有了这些假设,我们可以通过执行仿射变换来实现转换功能。每次我们想要执行转换时都创建转换矩阵会很浪费。出于这个原因,我们需要两个函数:第一个创建变换矩阵(只调用一次),第二个将使用该矩阵执行变换本身(可能调用多次,我们要变换的每个点一次) ,类似于:
tm = createTransformationMatrix(P1, P2, P4, w, h)
inPoint = (200, 50)
outPoint = transform(inPoint, tm)
请注意,我们只需要四个输入点中的三个即可在 2D 欧几里德中明确定义一个旋转矩形 space。
下面是 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();
我已经包含了所有的矩阵处理逻辑,因此这个代码片段是自包含的,但我建议您改用一些线性代数库来进行矩阵处理。
我也做了a more visual demo.