根据距离和方位从样线移动xy坐标
我有一艘船在水面上移动,寻找动物。船上有一个人站在船顶,面朝前方,当看到动物时,他会记录下船与动物之间的距离和方向。我有这些信息,还有在看到动物时船的xy坐标。我需要根据这些信息计算出动物的xy坐标。
不过,我没有船的原始方向,这让事情变得有些复杂;但我有船下一个GPS(xy)坐标,这样我可以计算出一个起始角度。然后,我可以根据看到动物时的方向来加或减这个角度,从而得到一个标准化的角度,这个角度可以用来通过三角函数找到动物的xy坐标。不幸的是,我的数学水平还不够好,无法完成这个任务。
我有几百个点,所以我需要把这个过程写成一个Python脚本,来处理所有的点。
总的来说,数据集中包含:
原始X,原始Y,结束(下一个)X,结束(下一个)Y,方向,距离
编辑:抱歉,我刚才说得不太清楚。
我认为这个问题可以分为三个阶段。
- 找到横断面的原始方向
- 找到这个点相对于横断面的方向
- 根据这个标准化的角度和从起始xy到点的距离,找到这个点的新坐标
我最开始写的Python代码在下面,虽然用处不大,给出的数字只是示例。
distFromBoat = 100
bearing = 45
lengthOpposite = origX-nextX
lengthAdjacent = origY - nextY
virtX = origX #virtual X
virtY = origY-lengthOpposite #virtual Y
angle = math.degrees(math.asin(math.radians((lengthOpposite/transectLen))))
newangle = angle + bearing
newLenAdj = math.cos(newangle)*distFromBoat
newLenOpp = math.sqrt(math.pow(distFromBoat,2) + math.pow(newLenAdj,2) - 2*(distFromBoat*newLenAdj)*(math.cos(newangle)))
newX = virtX-newLenOpp
newY = origY-newLenAdj
print str(newX) +"---"+str(newY)
提前感谢任何帮助!
3 个回答
这可能有更简单优雅的解决办法……前提是我理解你的问题没错。不过,这段代码可以帮你找到从你原来的位置到目标位置的方向:
(这里的输入是硬编码的,作为示例)
import math
origX = 0.0
origY = 0.0
nextX = 1.0
nextY = 0.0
Dist = ((nextX - origX)**2 + (nextY - origY)**2)**0.5
if origX == nextX and origY == nextY:
angle = 0
if origX == nextX and nextY < origY:
angle = 180
if nextY < origY and origX > nextX:
angle = math.degrees(math.asin((nextX -origX)/Dist)) - 90
if nextX > origX and nextY < origY:
angle = math.degrees(math.asin((nextX -origX)/Dist)) + 90
else:
angle = math.degrees(math.asin((nextX -origX)/Dist))
print angle
马特的函数有点问题,所以我用了 atan2
来帮你计算船的航向角度。
补充说明:这比我想的要复杂。最后你需要减去90度,并取反,这样才能把地理角度转换成三角函数角度。
(还有一个 angles
库(可能还有其他地理相关的库)里面已经有这个功能了。)
现在这个过程是这样的:它取 origX
和 origY
,找到三角函数角度,然后把它转换成 heading
,再把航向加到为横断面确定的角度上。接着,它会根据距离进行三角函数计算,但使用的是转换回三角函数度数的角度 -(X-90)
。这有点复杂,因为我们习惯把 0
度看作北方/上方,但在三角函数中是“向右”,而且三角函数是逆时针的,而导航是顺时针的。
import math
origX = 0.0
origY = 0.0
nextX = 0.0
nextY = -1.0
distance = 100.0
bearing = 45
def angle(origX,origY,nextX,nextY):
opp = float(nextY - origY)
adj = float(nextX - origX)
return(math.degrees(math.atan2(adj,opp)))
# atan2 seems to even work correctly (return zero) when origin equals next
transectAngle = angle(origX,origY,nextX,nextY) # assuming the function has been defined
print "bearing plus trans", transectAngle + bearing
trigAngle = -(transectAngle + bearing -90)
print "trig equiv angle", trigAngle
newX = origX + distance * math.cos(math.radians(trigAngle))
newY = origY + distance * math.sin(math.radians(trigAngle))
print "position",newX,newY
输出:
-70.7106781187 -70.7106781187
这里有一个函数可以打印出一堆测试案例(使用了全局变量,所以应该合并到上面的代码中)
def testcase():
bearinglist = [-45,45,135,-135]
dist = 10
for bearing in bearinglist:
print "----transect assuming relative bearing of {}------".format(bearing)
print "{:>6} {:>6} {:>6} {:>6} {:>6} {:>6} {:>6} {:>6}".format("x","y","tran","head","trigT","trigH","newX","newY")
for x in [0,.5,-.5]:
for y in [0,.5,1,-.5]:
# print "A:", x,y,angle(origX,origY,x,y)
tA = newangle(origX,origY,x,y)
trigA = -(tA-90)
heading = tA + bearing
trigHead = -(heading-90)
Xnew = distance * math.cos(math.radians(trigHead))
Ynew = distance * math.sin(math.radians(trigHead))
print "{:>6.1f} {:>6.1f} {:>6.1f} {:>6.1f} {:>6.1f} {:>6.1f} {:>6.1f} {:>6.1f}".format(x,y,tA,heading,trigA,trigHead,Xnew,Ynew)
根据我的理解,你的问题是这样的:
- 你有两个点,
start
和next
,你在这两个点之间移动。 - 你想找到一个第三个点,
New
,这个点应该在距离start
一定的距离和方向上,而你已经是从start
指向next
的方向。
我的解决方法是这样的:
- 先从
start
到next
创建一个标准化的向量。 - 然后根据给定的方向旋转这个标准化的向量。
- 接着把旋转后的标准化向量乘以你想要的距离,然后加到
start
上。 - 把
start
当作一个向量,最后的结果就是你的新点。
因为逆时针旋转(从当前点向“左”转)被认为是正方向,所以你需要把 bearing
反转,这样左舷就对应负值,右舷对应正值。
代码
import math
origX = 95485
origY = 729380
nextX = 95241
nextY = 729215
distance = 2000.0
bearing = 45
origVec = origX, origY
nextVec = nextX, nextY
#Euclidean distance between vectors (L2 norm)
dist = math.sqrt((nextVec[0] - origVec[0])**2 + (nextVec[1] - origVec[1])**2)
#Get a normalized difference vector
diffVec = (nextVec[0] - origVec[0])/dist, (nextVec[1] - origVec[1])/dist
#rotate our vector by bearing to get a vector from orig towards new point
#also, multiply by distance to get new value
#invert bearing, because +45 in math is counter-clockwise (left), not starboard
angle = math.radians(-bearing)
newVec = origVec[0]+(diffVec[0]*math.cos(angle) - diffVec[1]*math.sin(angle))*distance, \
origVec[1]+(diffVec[0]*math.sin(angle) + diffVec[1]*math.cos(angle))*distance
print newVec
输出:
(93521.29597031244, 729759.2973553676)