获取垂直于AB的坐标
Getting coordinates perpendicular to AB
我已经发布了这个问题here
感谢 Fang,我得到了解决方案,但问题是我需要使用的坐标是 GPS 坐标,显然 GPS 需要与笛卡尔坐标不同的公式。
所以我正在使用 Google Maps APIv3 多边形并具有 AB 坐标(通过使用多边形工具绘制它们)我单击位置 C,我需要将其移动到垂直于 AB 的 D CD 平行于 AB
所以问题是:
Having
A = 50.88269282423443,6.0036662220954895
B = 50.882753744583226,6.003803014755249
C = 50.88252571592428, 6.003832183778286
- D is perpendicular to AB
- CD is parallel to AB
求D的公式是什么
我已经尝试了很长时间来解决这个问题,但到目前为止没有成功。
所以 A,B,C
是已知的 'D' 是未知的。
轴CD
- 是
cd(t1)=C+(B-A)*t1
- 其中:
cd(t1)
是 CD
上的任意一点
t1
是区间 <-inf,+inf>
的参数
轴BD
- 是
bd(t2)=B+Q*t2
- 其中:
bd(t2)
是 BD
上的任意一点
t2
是区间 <-inf,+inf>
的参数
Q
是垂直于 B-A
的向量
- 在2D中你可以这样获取:
Q.x=+(B-A).y
Q.y=-(B-A).x
- 在 3D 中使用叉积,但您的示例暗示 2D 情况...
D点
- 是
BD
和CD
的交集
- 所以只要代数求解这个:
I. cd(t1)=C+(B-A)*t1
II. bd(t2)=B+Q*t2
III. cd(t1)=bd(t2)
- 在 2D 中,这 (
III.
) 导致具有 2 个变量的 2 个线性方程 ...
C.x+(B.x-A.x)*t1=B.x+(B.y-A.y)*t2
C.y+(B.y-A.y)*t1=B.y-(B.x-A.x)*t2
- 找到参数
t1
然后计算D=cd(t1)
- 或
t2
然后计算D=bd(t2)
- 你应该推导两种解决方案并使用精度更高的解决方案
- 两者都会使用一些除法
A1/A2
所以选择一个更大的|A2|
如果你的D点也能锁到A点
- 然后找到两个位置
D1
锁定到A
和D2
锁定到B
- 并选择了离点
C
(min(|D1-C|,|D2-C|)
) 较近的一个
- 为了简化这一点,您可以使用
D1+(B-A)=D2
...
[edit2] 我确实在 edit1 的某个地方犯了一些错误所以这里是工作版本
double ax,ay,bx,by,cx,cy,dx,dy; // points
double bdx,bdy,cdx,cdy; // directions
double t1,t2; // parameters
/*
//--- intersection equations ----------------
1. cx+cdx*t1=bx+bdx*t2;
2. cy+cdy*t1=by+bdy*t2;
//--- separate t1 ---------------------------
1. t1=(bx-cx+(bdx*t2)/cdx;
//--- substitute t1 and separate t2 ---------
2. t2=(cy-by+((bx-cx)*cdy/cdx))/(bdy-(bdx*cdy/cdx));
//-------------------------------------------
//--- separate t2 ---------------------------
1. t2=(cx-bx+cdx*t1)/bdx;
//--- substitute t2 and separate t1 ---------
2. t1=(by-cy+((cx-bx)*bdy/bdx))/(cdy-(cdx*bdy/bdx));
//-------------------------------------------
*/
// common
cdx=bx-ax;
cdy=by-ay;
bdx=+cdy;
bdy=-cdx;
//solution 1
t2=(cy-by+((bx-cx)*cdy/cdx))/(bdy-(bdx*cdy/cdx));
dx=bx+bdx*t2;
dy=by+bdy*t2;
//solution 2
t1=(by-cy+((cx-bx)*bdy/bdx))/(cdy-(cdx*bdy/bdx));
dx=cx+cdx*t1;
dy=cy+cdy*t1;
- 只使用不除以零的解...
在你之前关于多边形的问题之后,我开始制作一些东西,一个 javascript 对象。它将在这里展示它的用途。
我在那里 post 编辑了它(我在这里跳过了 post 中的文档,请阅读那里的文档):Mercator Projection slightly off
我先post代码,我稍后解释。
<title>Getting coordinates perpendicular to AB</title>
<div id="log"></div>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry"></script>
<script>
Earth = {
// @see http://www.space.com/17638-how-big-is-earth.html for the data
// along the equator
circumference_equator: 40075000,
// throught both poles.
// Note: this is basically the original definition of the meter; they were 2km off on a distance from pole to equator ( http://en.wikipedia.org/wiki/History_of_the_metre )
circumference_poles: 40008000,
// given a change in latitude, how many meters did you move?
lat2Y: function(dLat) {
return this.circumference_poles / 360 * dLat;
},
// given a change in longitude and a given latitude, how many meters did you move?
lng2X: function(dLng, lat) {
return Math.cos( this.deg2rad(lat) ) * (this.circumference_poles / 360 * dLng);
},
// given a distance you move due North (or South), what's the new coordinates?
// returns a change in latitude
y2Lat: function(y) {
return y * 360 / this.circumference_poles;
},
// given a distance you move due East (or West) and a given latitude, what's the new coordinates?
// returns a change in longitude
x2Lng: function(x, lat) {
return x * 360 / ( Math.cos( this.deg2rad(lat) ) * this.circumference_poles);
},
// (360°) degrees to radials
deg2rad: function(deg) {
return deg * Math.PI / 180;
},
// returns a change in position
xy2LatLng: function(y, x, lat) {
return {
lat: this.y2Lat(y),
lng: this.x2Lng(x, lat)
};
},
// @param heading: North = 0; east = 90°; ...
setHeading: function(lat, lng, dist, heading) {
var latDestination = lat + this.y2Lat(dist * Math.cos(this.deg2rad(heading)));
var lngDestination = lng + this.x2Lng(dist * Math.sin(this.deg2rad(heading)), lat);
return {
lat: latDestination,
lng: lngDestination
};
},
// returns the absolute position
moveByXY: function(lat, lng, x, y) {
var dLatLng = Earth.xy2LatLng(x, y, lat);
latLng = [dLatLng.lat, dLatLng.lng ];
return {
lat: lat + latLng[0],
lng: lng + latLng[1]
}
}
}
/**
* returns the shortest distance between a point p and a line segment (u,v).
* based on
*/
function distToSegment(p, v, w) {
return Math.sqrt(distToSegmentSquared(p, v, w));
function distToSegmentSquared(p, v, w) {
var l2 = dist2(v, w);
if (l2 == 0) {return dist2(p, v);}
var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0) {return dist2(p, v);}
if (t > 1) {return dist2(p, w);}
return dist2(p,
{x: v.x + t * (w.x - v.x),
y: v.y + t * (w.y - v.y)}
);
}
function sqr(x) {
return x * x ;
}
function dist2(v, w) {
return sqr(v.x - w.x) + sqr(v.y - w.y);
}
}
</script>
<script>
var A = {lat: 50.88269282423443, lng: 6.0036662220954895};
var B = {lat: 50.882753744583226, lng: 6.003803014755249};
var C = {lat: 50.88252571592428, lng: 6.003832183778286};
// get the angle of AB (let Google calculate it)
var angle_ab = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(B.lat, B.lng)
);
// we convert these coordinates to metric units. lat goes along y; lng goes along x
// so this tells us that from A to B there are X metres eastwards, Y metres northwards.
var a = {x:0, y:0};
var b = {
x: Earth.lng2X(B.lng - A.lng, A.lat),
y: Earth.lat2Y(B.lat - A.lat),
};
var c = {
x: Earth.lng2X(C.lng - A.lng, A.lat),
y: Earth.lat2Y(C.lat - A.lat),
};
// second, we look for point E, being the projection of C on AB
var dist_E = distToSegment(c, a, b);
// Now we know this: if we move from B, distance "dist_E" on an angle 90° to the right (anti-clockwise) of AB
var D = Earth.setHeading(B.lat, B.lng, dist_E, angle_ab + 90);
log('distance of E (= projection of C on AB) to AB: <b>' + dist_E +'</b>m');
log('Point D: <b>' + D.lat +','+ D.lng +'</b>');
function log(text) {
document.getElementById('log').innerHTML += text + '<br>';
}
</script>
我做了什么:
首先我将数据从坐标转换为米
我找到E点:C在AB上的投影
CE的距离和角度和BD一样,所以我可以用Earth.setHeading(),从B.
注意:
您的问题中没有矩形,但仍然请注意:
曲面上没有矩形这样的东西;完全准确地制作那个矩形是不可能的。如果你向前走 x 距离,然后向右转 90° 并重复 4 次,你将不会(准确地)回到你开始的地方。
在球体上,矩形的内角和大于360°;三角形的内角和将大于 180°。
简单示例:取点 (lat, lng) 0,0 ; 0,90 ; 90,0(赤道两点+北极);那是一个内角和 = 270° 的三角形。
因此,无论您寻找什么答案,都将是一个近似值。距离越大,结果越不准确(不管是什么天才解决的问题);
您不能简单地假设图表上的每个直角在地球表面都是直角。
感谢 Emmanuel,我找到了球形函数,这对我制作函数有很大帮助。我也喜欢他的回答,但我最终做出的代码少了很多。
this.makeRightAngle = function(polygon, e, A, B, C){
var heading_AB = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(B.lat, B.lng)
);
var heading_AC = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var heading_BC = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var distanceBC = google.maps.geometry.spherical.computeDistanceBetween(
new google.maps.LatLng(B.lat, B.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var heading_C_AB = heading_AC - heading_AB;
if((heading_C_AB < 0 && heading_C_AB > -200) || (heading_C_AB > 200)){
var new_heading = heading_AB - 90;
}
else{
var new_heading = heading_AB + 90;
}
var D = google.maps.geometry.spherical.computeOffset(
new google.maps.LatLng(B.lat, B.lng), //From LatLng
distanceBC, //Get distance BC
new_heading //Heading of AB + or - 90 degrees
);
var p = {x: D.lat(), y: D.lng()};
var latlng = new google.maps.LatLng(p.x, p.y);
return latlng;
}
仍然非常感谢您的回答!
我已经发布了这个问题here
感谢 Fang,我得到了解决方案,但问题是我需要使用的坐标是 GPS 坐标,显然 GPS 需要与笛卡尔坐标不同的公式。
所以我正在使用 Google Maps APIv3 多边形并具有 AB 坐标(通过使用多边形工具绘制它们)我单击位置 C,我需要将其移动到垂直于 AB 的 D CD 平行于 AB
所以问题是:
Having
A = 50.88269282423443,6.0036662220954895
B = 50.882753744583226,6.003803014755249
C = 50.88252571592428, 6.003832183778286
- D is perpendicular to AB
- CD is parallel to AB
求D的公式是什么
我已经尝试了很长时间来解决这个问题,但到目前为止没有成功。
所以 A,B,C
是已知的 'D' 是未知的。
轴CD
- 是
cd(t1)=C+(B-A)*t1
- 其中:
cd(t1)
是CD
上的任意一点
t1
是区间<-inf,+inf>
的参数
轴BD
- 是
bd(t2)=B+Q*t2
- 其中:
bd(t2)
是BD
上的任意一点
t2
是区间<-inf,+inf>
的参数
Q
是垂直于B-A
的向量
- 在2D中你可以这样获取:
Q.x=+(B-A).y
Q.y=-(B-A).x
- 在 3D 中使用叉积,但您的示例暗示 2D 情况...
D点
- 是
BD
和CD
的交集
- 所以只要代数求解这个:
I. cd(t1)=C+(B-A)*t1
II. bd(t2)=B+Q*t2
III. cd(t1)=bd(t2)
- 在 2D 中,这 (
III.
) 导致具有 2 个变量的 2 个线性方程 ... C.x+(B.x-A.x)*t1=B.x+(B.y-A.y)*t2
C.y+(B.y-A.y)*t1=B.y-(B.x-A.x)*t2
- 找到参数
t1
然后计算D=cd(t1)
- 或
t2
然后计算D=bd(t2)
- 你应该推导两种解决方案并使用精度更高的解决方案
- 两者都会使用一些除法
A1/A2
所以选择一个更大的|A2|
如果你的D点也能锁到A点
- 然后找到两个位置
D1
锁定到A
和D2
锁定到B
- 并选择了离点
C
(min(|D1-C|,|D2-C|)
) 较近的一个
- 为了简化这一点,您可以使用
D1+(B-A)=D2
...
[edit2] 我确实在 edit1 的某个地方犯了一些错误所以这里是工作版本
double ax,ay,bx,by,cx,cy,dx,dy; // points
double bdx,bdy,cdx,cdy; // directions
double t1,t2; // parameters
/*
//--- intersection equations ----------------
1. cx+cdx*t1=bx+bdx*t2;
2. cy+cdy*t1=by+bdy*t2;
//--- separate t1 ---------------------------
1. t1=(bx-cx+(bdx*t2)/cdx;
//--- substitute t1 and separate t2 ---------
2. t2=(cy-by+((bx-cx)*cdy/cdx))/(bdy-(bdx*cdy/cdx));
//-------------------------------------------
//--- separate t2 ---------------------------
1. t2=(cx-bx+cdx*t1)/bdx;
//--- substitute t2 and separate t1 ---------
2. t1=(by-cy+((cx-bx)*bdy/bdx))/(cdy-(cdx*bdy/bdx));
//-------------------------------------------
*/
// common
cdx=bx-ax;
cdy=by-ay;
bdx=+cdy;
bdy=-cdx;
//solution 1
t2=(cy-by+((bx-cx)*cdy/cdx))/(bdy-(bdx*cdy/cdx));
dx=bx+bdx*t2;
dy=by+bdy*t2;
//solution 2
t1=(by-cy+((cx-bx)*bdy/bdx))/(cdy-(cdx*bdy/bdx));
dx=cx+cdx*t1;
dy=cy+cdy*t1;
- 只使用不除以零的解...
在你之前关于多边形的问题之后,我开始制作一些东西,一个 javascript 对象。它将在这里展示它的用途。 我在那里 post 编辑了它(我在这里跳过了 post 中的文档,请阅读那里的文档):Mercator Projection slightly off
我先post代码,我稍后解释。
<title>Getting coordinates perpendicular to AB</title>
<div id="log"></div>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry"></script>
<script>
Earth = {
// @see http://www.space.com/17638-how-big-is-earth.html for the data
// along the equator
circumference_equator: 40075000,
// throught both poles.
// Note: this is basically the original definition of the meter; they were 2km off on a distance from pole to equator ( http://en.wikipedia.org/wiki/History_of_the_metre )
circumference_poles: 40008000,
// given a change in latitude, how many meters did you move?
lat2Y: function(dLat) {
return this.circumference_poles / 360 * dLat;
},
// given a change in longitude and a given latitude, how many meters did you move?
lng2X: function(dLng, lat) {
return Math.cos( this.deg2rad(lat) ) * (this.circumference_poles / 360 * dLng);
},
// given a distance you move due North (or South), what's the new coordinates?
// returns a change in latitude
y2Lat: function(y) {
return y * 360 / this.circumference_poles;
},
// given a distance you move due East (or West) and a given latitude, what's the new coordinates?
// returns a change in longitude
x2Lng: function(x, lat) {
return x * 360 / ( Math.cos( this.deg2rad(lat) ) * this.circumference_poles);
},
// (360°) degrees to radials
deg2rad: function(deg) {
return deg * Math.PI / 180;
},
// returns a change in position
xy2LatLng: function(y, x, lat) {
return {
lat: this.y2Lat(y),
lng: this.x2Lng(x, lat)
};
},
// @param heading: North = 0; east = 90°; ...
setHeading: function(lat, lng, dist, heading) {
var latDestination = lat + this.y2Lat(dist * Math.cos(this.deg2rad(heading)));
var lngDestination = lng + this.x2Lng(dist * Math.sin(this.deg2rad(heading)), lat);
return {
lat: latDestination,
lng: lngDestination
};
},
// returns the absolute position
moveByXY: function(lat, lng, x, y) {
var dLatLng = Earth.xy2LatLng(x, y, lat);
latLng = [dLatLng.lat, dLatLng.lng ];
return {
lat: lat + latLng[0],
lng: lng + latLng[1]
}
}
}
/**
* returns the shortest distance between a point p and a line segment (u,v).
* based on
*/
function distToSegment(p, v, w) {
return Math.sqrt(distToSegmentSquared(p, v, w));
function distToSegmentSquared(p, v, w) {
var l2 = dist2(v, w);
if (l2 == 0) {return dist2(p, v);}
var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0) {return dist2(p, v);}
if (t > 1) {return dist2(p, w);}
return dist2(p,
{x: v.x + t * (w.x - v.x),
y: v.y + t * (w.y - v.y)}
);
}
function sqr(x) {
return x * x ;
}
function dist2(v, w) {
return sqr(v.x - w.x) + sqr(v.y - w.y);
}
}
</script>
<script>
var A = {lat: 50.88269282423443, lng: 6.0036662220954895};
var B = {lat: 50.882753744583226, lng: 6.003803014755249};
var C = {lat: 50.88252571592428, lng: 6.003832183778286};
// get the angle of AB (let Google calculate it)
var angle_ab = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(B.lat, B.lng)
);
// we convert these coordinates to metric units. lat goes along y; lng goes along x
// so this tells us that from A to B there are X metres eastwards, Y metres northwards.
var a = {x:0, y:0};
var b = {
x: Earth.lng2X(B.lng - A.lng, A.lat),
y: Earth.lat2Y(B.lat - A.lat),
};
var c = {
x: Earth.lng2X(C.lng - A.lng, A.lat),
y: Earth.lat2Y(C.lat - A.lat),
};
// second, we look for point E, being the projection of C on AB
var dist_E = distToSegment(c, a, b);
// Now we know this: if we move from B, distance "dist_E" on an angle 90° to the right (anti-clockwise) of AB
var D = Earth.setHeading(B.lat, B.lng, dist_E, angle_ab + 90);
log('distance of E (= projection of C on AB) to AB: <b>' + dist_E +'</b>m');
log('Point D: <b>' + D.lat +','+ D.lng +'</b>');
function log(text) {
document.getElementById('log').innerHTML += text + '<br>';
}
</script>
我做了什么:
首先我将数据从坐标转换为米
我找到E点:C在AB上的投影
CE的距离和角度和BD一样,所以我可以用Earth.setHeading(),从B.
注意: 您的问题中没有矩形,但仍然请注意: 曲面上没有矩形这样的东西;完全准确地制作那个矩形是不可能的。如果你向前走 x 距离,然后向右转 90° 并重复 4 次,你将不会(准确地)回到你开始的地方。
在球体上,矩形的内角和大于360°;三角形的内角和将大于 180°。 简单示例:取点 (lat, lng) 0,0 ; 0,90 ; 90,0(赤道两点+北极);那是一个内角和 = 270° 的三角形。
因此,无论您寻找什么答案,都将是一个近似值。距离越大,结果越不准确(不管是什么天才解决的问题);
您不能简单地假设图表上的每个直角在地球表面都是直角。
感谢 Emmanuel,我找到了球形函数,这对我制作函数有很大帮助。我也喜欢他的回答,但我最终做出的代码少了很多。
this.makeRightAngle = function(polygon, e, A, B, C){
var heading_AB = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(B.lat, B.lng)
);
var heading_AC = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var heading_BC = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var distanceBC = google.maps.geometry.spherical.computeDistanceBetween(
new google.maps.LatLng(B.lat, B.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var heading_C_AB = heading_AC - heading_AB;
if((heading_C_AB < 0 && heading_C_AB > -200) || (heading_C_AB > 200)){
var new_heading = heading_AB - 90;
}
else{
var new_heading = heading_AB + 90;
}
var D = google.maps.geometry.spherical.computeOffset(
new google.maps.LatLng(B.lat, B.lng), //From LatLng
distanceBC, //Get distance BC
new_heading //Heading of AB + or - 90 degrees
);
var p = {x: D.lat(), y: D.lng()};
var latlng = new google.maps.LatLng(p.x, p.y);
return latlng;
}
仍然非常感谢您的回答!