Вопрос

I have a boat moving along a transect, looking for animals. Someone is stood on the top of the boat, facing forward, and is logging the distance from the boat and the bearing from the front of the boat when an animal is seen. I have this information as well as the xy coordinates of the boat at the point at which the animal was seen. I need to get the xy coordinates of the animal itself based on this information.

I don't have the original compass bearing of the boat, which makes this tricky; but what I do have is the next GPS (xy) coordinate of the boat, from which I can calculate a starting angle. From this, it should be possible to add or subtract the bearing at which the animal was seen to give a normalised angle which can be used to find the xy coordinates of the animal using trigonometry. Unfortunately my maths skills aren't quite up to the job.

I have several hundred points so I need to put this into a Python script to go through all the points.

In summary, the dataset has:

Original X, Original Y, End(next) X, End(next) Y, Bearing, Distance

EDIT: Sorry, I was in a rush and didn't explain this very well.

I see there being 3 stages to this problem.

  1. Finding the original bearing of the transect
  2. Finding the bearing of the point relative to the transect
  3. Finding the new coordinates of the point based on this normalised angle and distance from the boat at the start xy

The Python code I had originally is below, although it's not much use - the figures given are examples.

    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)

Thanks for any help in advance!

Это было полезно?

Решение 2

From what I understand, this is your problem:

  • You have 2 points, start and next that you are walking between
  • You want to find the coordinates of a third point, New that should be some distance and bearing from start, given that you are already facing from start to next.

My solution is this:

  • Create a normalized vector from start to next
  • Rotate your normalized vector by given bearing
  • Multiply the normalized, rotated vector by your distance, and then add it to start
  • treating start as a vector, the result of the addition is your new point

Because a counter-clockwise rotation ("left" from the current point) is considered positive, you need to invert bearing so that port is associated with negative, and starboard with positive.

Code

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

Output:

(93521.29597031244, 729759.2973553676)

Другие советы

There is a little problem with Matt's function, so I used atan2 to get you the angle that the boat is going.

Edit: That was more complicated than I expected. In the end you need to subtract 90 and take the inverse to go from the geo-referenced angles to the trig angles.

(There is also an angles library (and probably other geography ones) that have this built in.

Now this takes the origX and origY, finds the trig angle and converts it to a heading, adds the bearing to the angle determined for the transect. Then it does trig on the distance, but using the angle converted back to trig degrees -(X-90). It is kind of warped, because we are used to thinking of 0 degrees as north/up, but in trig it is "to the right", and trig goes counter clockwise vs clockwise for navigation.

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

Output:

-70.7106781187 -70.7106781187

Here is a function to print out a bunch of test cases (uses global vars so should be folded into the code above)

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)

enter image description here adding paths

It's (probably) possible there's a more elegant solution than this...assuming I understood your issue correctly. But, this will give you the bearing from your original location:

(inputs hard-coded as an example)

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top