Skip to content

Instantly share code, notes, and snippets.

@TimelessP
Created July 30, 2022 08:30
Show Gist options
  • Save TimelessP/73dd7e2111f66a1e8294f886f74f7db6 to your computer and use it in GitHub Desktop.
Save TimelessP/73dd7e2111f66a1e8294f886f74f7db6 to your computer and use it in GitHub Desktop.
haversinehello.py is some slightly-edited code that was generated by openai after some prompting by me. The functionality is mainly around calculating vessel movement around a sphere. Caution, there are calculation bugs in this gist that need fixing.
import math
def calculate_new_coordinate(ship_coordinate, ship_heading, ship_speed, time_elapsed_seconds, heading_hold=False,
depth_metres=0):
"""
Calculates the new coordinates of the ship (lat, long), given the current heading, speed in knots,
and the time elapsed in seconds. A spherical Earth model is used, with no tides.
:param ship_coordinate: (lat, long) in range -90 <= lat <= 90, -180 <= long <= 180.
:param ship_heading: heading in degrees in range 0 <= heading <= 360.
:param ship_speed: speed in knots in the range -999 <= speed <= 999.
:param time_elapsed_seconds: time elapsed in seconds from the ship coordinates at the current heading and speed.
:param heading_hold: True if the ship is to maintain its heading, False if the ship is to change its heading.
:param depth_metres: depth in metres in the range 0 <= depth <= 99999.
:return: the new coordinates (lat, long) in range -90 <= lat <= 90, -180 <= long <= 180.
"""
# Earth's diameter in km:
earth_diameter = 12742
depth_km = depth_metres / 1000
spherical_diameter = earth_diameter - depth_km
# Convert the ship heading to radians.
ship_heading_radians = ship_heading * (math.pi / 180)
# Convert the ship speed to "meters per second".
ship_speed_meters_per_second = ship_speed * 0.514444444
# Calculate the distance traveled.
distance_traveled = ship_speed_meters_per_second * time_elapsed_seconds
# Calculate the new latitude, factoring in the size of the planet.
new_latitude = ship_coordinate[0] + (distance_traveled * math.cos(ship_heading_radians) / spherical_diameter)
# Calculate the new longitude, factoring in the size of the planet.
new_longitude = ship_coordinate[1] + (distance_traveled * math.sin(ship_heading_radians) / spherical_diameter)
# Return the new coordinates.
new_coordinates = (new_latitude, new_longitude)
# If heading hold is False then calculate the new heading.
# This prevents simulating the ship going in spirals towards the poles on the sphere.
if heading_hold is False:
# Calculate the new heading.
new_heading = math.atan2(new_coordinates[1] - ship_coordinate[1], new_coordinates[0] - ship_coordinate[0]) *\
(180 / math.pi)
# Return the new heading.
return new_coordinates, new_heading
return new_coordinates, ship_heading
def calculate_new_heading(ship_coordinate, waypoint):
"""
Calculates the new heading of the ship so that the vessel can reach the waypoint in the shortest distance
on a spherical Earth model. A spherical Earth model is used, with no tides.
:param ship_coordinate: (lat, long) in range -90 <= lat <= 90, -180 <= long <= 180.
:param waypoint: (lat, long) in range -90 <= lat <= 90, -180 <= long <= 180.
:return: the new heading in degrees in range 0 <= heading <= 360.
"""
# Calculate the new heading.
new_heading = math.atan2(waypoint[1] - ship_coordinate[1], waypoint[0] - ship_coordinate[0]) * (180 / math.pi)
# Return the new heading.
return new_heading
# TODO: Fix this calculation.
def calculate_distance_to_waypoint(ship_coordinate, waypoint, depth_metres):
"""
Calculates the distance to the waypoint in nautical miles.
:param ship_coordinate: (lat, long) in range -90 <= lat <= 90, -180 <= long <= 180.
:param waypoint: (lat, long) in range -90 <= lat <= 90, -180 <= long <= 180.
:param depth_metres: depth in metres in the range 0 <= depth <= 99999.
:return: the distance to the waypoint in nautical miles.
"""
# Earth's diameter in km:
earth_diameter = 12742
depth_km = depth_metres / 1000
# Diameter of the spherical Earth model in kilometres.
spherical_diameter_km = earth_diameter - depth_km
# Convert to nautical miles.
spherical_diameter_nm = spherical_diameter_km * 0.539956803456
# Calculate the haversine distance to the waypoint in nautical miles, factoring in the Earth's diameter.
distance_to_waypoint = spherical_diameter_nm * math.acos(
math.sin(math.radians(ship_coordinate[0])) * math.sin(math.radians(waypoint[0])) +
math.cos(math.radians(ship_coordinate[0])) * math.cos(math.radians(waypoint[0])) *
math.cos(math.radians(ship_coordinate[1] - waypoint[1])))
# distance_to_waypoint = spherical_diameter_km * math.pi * math.sqrt(
# (math.pow(waypoint[0] - ship_coordinate[0], 2) + math.pow(waypoint[1] - ship_coordinate[1], 2))) / 1852
# distance_to_waypoint = (spherical_diameter_km * math.pi) / 180 * math.acos(
# math.sin(ship_coordinate[0] * (math.pi / 180)) * math.sin(waypoint[0] * (math.pi / 180)) + math.cos(
# ship_coordinate[0] * (math.pi / 180)) * math.cos(waypoint[0] * (math.pi / 180)) * math.cos(
# ship_coordinate[1] * (math.pi / 180) - waypoint[1] * (math.pi / 180)))
# Return the distance to the waypoint in nautical miles.
return distance_to_waypoint
if __name__ == '__main__':
# Set depth to 0 metres.
depth_metres = 0
# Define a coordinate in WGS84 of the ship on the ocean.
# The coordinate is in the form of a tuple (latitude, longitude)
# The latitude is in the range [-90, 90] and the longitude in [-180, 180]
ship_coordinate = (0.0, -1.0)
# Define the ship heading in degrees.
# The heading is in the range [0, 360]
ship_heading = 45.0
# Define the ship speed in knots.
# The speed is in the range [0, 100]
ship_speed = 10.0
# Calculate the new coordinate of the ship after the time has passed.
# The time is in the range [0, 600] in seconds.
# The result is in the form of a tuple (latitude, longitude)
time_elapsed_secs = 60.0
new_coordinate = calculate_new_coordinate(ship_coordinate, ship_heading, ship_speed, time_elapsed_secs,
heading_hold=False, depth_metres=depth_metres)
# Print the new coordinate
print(new_coordinate)
# Define a waypoint as a lat, long tuple.
waypoint = (45.0, 90.0)
# Calculate the new heading to the waypoint.
# The result is in the range [0, 360]
ship_heading = calculate_new_heading(ship_coordinate, waypoint)
print(ship_heading)
distance_to_wp = calculate_distance_to_waypoint(ship_coordinate, waypoint, depth_metres)
print(f"Distance to waypoint: {distance_to_wp} nautical miles")
# Calculate the circumference from the diameter.
earth_diameter = 12742
circumference = earth_diameter * math.pi
# Convert the circumference from kilometres to nautical miles.
circumference_nm = circumference * 0.539957
# Calculate the distance from pole to pole.
# The result is in the range [0, earth_circumference_nm]
distance_from_pole_to_pole = circumference_nm / 2
print(f"Distance from pole to pole: {distance_from_pole_to_pole} nautical miles")
north_pole = (90.0, 0.0)
south_pole = (-90.0, 0.0)
distance_to_pole = calculate_distance_to_waypoint(south_pole, north_pole, depth_metres)
print(f"Distance to pole: {distance_to_pole} nautical miles")
@TimelessP
Copy link
Author

It's a mess, I know, but I'm dumping the generated code here because I'd really hate to lose it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment