def midpt(invoyage): ''' Given an object of :class:`.Voyage` interpolate between alternate reports and compare the interpolated location to the actual location. e.g. take difference between reports 2 and 4 and interpolate to get an estimate for the position at the time of report 3. Then compare the estimated and actual positions at the time of report 3. :param invoyage: Ship :class:`.Voyage` :type invoyage: :class:`.Voyage` :return: list of distances from estimated positions in km :rtype: list of floats The calculation linearly interpolates the latitudes and longitudes (allowing for wrapping around the dateline and so on). ''' # Compute distance from time-proportional position between # outside reported positions to middle reported position. nobs = len(invoyage) midpoint_discrepancies = [None] for i in range(1, nobs-1): t0 = invoyage.getvar(i, 'time_diff') t1 = invoyage.getvar(i+1, 'time_diff') if (t0 != None and t1 != None): if t0 + t1 != 0: fraction_of_time_diff = t0 / (t0 + t1) else: fraction_of_time_diff = 0.0 else: fraction_of_time_diff = 0.0 if fraction_of_time_diff > 1.0: print fraction_of_time_diff,t0,t1 estimated_lat_at_midpt, estimated_lon_at_midpt =\ sph.intermediate_point(invoyage.getvar(i-1, 'LAT'),invoyage.getvar(i-1, 'LON'), invoyage.getvar(i+1, 'LAT'),invoyage.getvar(i+1, 'LON'), fraction_of_time_diff) # Time-proportional position is at AT3 (N/S) and AN3 (E/W) # Reported mid-point will be at AT4 (N/S) and AN4 (E/W) # Compute distance between time/prop posn and reported mid-point. # SIDE1 is distance travelled North/South discrepancy = sph.sphere_distance(invoyage.getvar(i, 'LAT'), invoyage.getvar(i, 'LON'), estimated_lat_at_midpt, estimated_lon_at_midpt) midpoint_discrepancies.append(discrepancy) midpoint_discrepancies.append(None) return midpoint_discrepancies
def midpt(invoyage): """ Given an object of :class:`.Voyage` interpolate between alternate reports and compare the interpolated location to the actual location. e.g. take difference between reports 2 and 4 and interpolate to get an estimate for the position at the time of report 3. Then compare the estimated and actual positions at the time of report 3. :param invoyage: Ship :class:`.Voyage` :type invoyage: :class:`.Voyage` :return: list of distances from estimated positions in km :rtype: list of floats The calculation linearly interpolates the latitudes and longitudes (allowing for wrapping around the dateline and so on). """ # Compute distance from time-proportional position between # outside reported positions to middle reported position. nobs = len(invoyage) midpoint_discrepancies = [None] for i in range(1, nobs - 1): t0 = invoyage.getvar(i, 'time_diff') t1 = invoyage.getvar(i + 1, 'time_diff') if t0 is not None and t1 is not None: if t0 + t1 != 0: fraction_of_time_diff = t0 / (t0 + t1) else: fraction_of_time_diff = 0.0 else: fraction_of_time_diff = 0.0 if fraction_of_time_diff > 1.0: print(fraction_of_time_diff, t0, t1) estimated_lat_at_midpt, estimated_lon_at_midpt = \ sph.intermediate_point(invoyage.getvar(i - 1, 'LAT'), invoyage.getvar(i - 1, 'LON'), invoyage.getvar(i + 1, 'LAT'), invoyage.getvar(i + 1, 'LON'), fraction_of_time_diff) # Time-proportional position is at AT3 (N/S) and AN3 (E/W) # Reported mid-point will be at AT4 (N/S) and AN4 (E/W) # Compute distance between time/prop posn and reported mid-point. # SIDE1 is distance travelled North/South discrepancy = sph.sphere_distance(invoyage.getvar(i, 'LAT'), invoyage.getvar(i, 'LON'), estimated_lat_at_midpt, estimated_lon_at_midpt) midpoint_discrepancies.append(discrepancy) midpoint_discrepancies.append(None) return midpoint_discrepancies
def test_ship_heading_east_at_60knots_at_latitude60_goes2degrees_in_1hour(self): ''' A ship travelling east at 60 knots will go 2 degrees in 1 hour at 60N ''' km_to_nm = 0.539957 alat = 60.0 alon = 0.0 avs = 60.0/km_to_nm ads = 90.0 timdif = 2.0 dlat,dlon = tc.increment_position(alat,alon,avs,ads,timdif) distance = sph.sphere_distance(alat,alon,alat+dlat,alon+dlon) * km_to_nm self.assertAlmostEqual(distance, 60, delta=0.0001)
def distr2(invoyage): """ Given a list of :class:`.Voyage` , calculate what the distance is between the projected position (based on the reported speed and heading at the current and previous time steps) and the actual position. The calculation proceeds from the final, later observation to the first (in contrast to distr1 which runs in time order) :param invoyage: List of :class:`.Voyage` :type invoyage: :class:`.Voyage` :return: list of distances from estimated positions :rtype: list of floats This takes the speed and direction reported by the ship and projects it forwards half a time step, it then projects it forwards another half time step using the speed and direction for the next report, to which the projected location is then compared. The distances between the projeted and actual locations is returned """ # Compute difference between actual and expected positions after # two observations IN REVERSE ORDER # Each vessel travels at its reported speed and direction for # half the time interval; the difference (in miles) between # the calculated and observed positions is calculatered. km_to_nm = 0.539957 nobs = len(invoyage) distance_from_est_location = [None] for i in range(nobs - 1, 0, -1): if (invoyage.getvar(i, 'vsi') is not None and invoyage.getvar(i - 1, 'vsi') is not None and invoyage.getvar(i, 'dsi') is not None and invoyage.getvar(i - 1, 'dsi') is not None and invoyage.getvar(i, 'time_diff') is not None): # get increment from initial position - backwards in time # means reversing the direction by 180 degrees lat1, lon1 = increment_position( invoyage.getvar(i, 'LAT'), invoyage.getvar(i, 'LON'), invoyage.getvar(i, 'vsi') / km_to_nm, invoyage.getvar(i, 'dsi') - 180., invoyage.getvar(i, 'time_diff')) lat2, lon2 = increment_position( invoyage.getvar(i - 1, 'LAT'), invoyage.getvar(i - 1, 'LON'), invoyage.getvar(i - 1, 'vsi') / km_to_nm, invoyage.getvar(i - 1, 'dsi') - 180., invoyage.getvar(i, 'time_diff')) # apply increments to the lat and lon at i-1 alatx = invoyage.getvar(i, 'LAT') + lat1 + lat2 alonx = invoyage.getvar(i, 'LON') + lon1 + lon2 # calculate distance between calculated position and the second reported position discrepancy = sph.sphere_distance(invoyage.getvar(i - 1, 'LAT'), invoyage.getvar(i - 1, 'LON'), alatx, alonx) distance_from_est_location.append(discrepancy) else: # in the absence of reported speed and direction set to None distance_from_est_location.append(None) # that fancy bit at the end reverses the array return distance_from_est_location[::-1]
def split_generic_callsign(invoyage): ''' Prototype function to identify when a callsign is being used by multiple ships and to split the observations into pseudo IDs that each represents a different ship :param invoyage: a voyage object containing marine reports :type invoyage: Voyage :return: list of separate Voyages that the input Voyage has been split into. :return type: Voyage The function works by comparing consecutive observations in the input lists and calculating the implied speed. If it is greater than 40 knots, a new ship is generated. Each subsequent observation is assigned to the closest ship unless the speed exceed 40 knots. If it does, a new ship is generated. ''' knots_conversion = 0.539957 if len(invoyage) <= 0: return [] result = [1] n_ships = 1 outvoyages = [ex.Voyage()] outvoyages[0].add_report(invoyage.getrep(0)) ntimes = len(invoyage) if ntimes > 1: for i in range(1, ntimes): #calculate speeds from last position for each ship speeds = [] distances = [] for j in range(0, n_ships): last_rep = outvoyages[j].last_rep() speed, distance, course, timediff = invoyage.getrep(i)-last_rep #calc predicted position and distance of new ob from predicted position pred_lat, pred_lon = outvoyages[j].predict_next_point(timediff) dist = sph.sphere_distance(invoyage.getvar(i, 'LAT'), invoyage.getvar(i, 'LON'), pred_lat, pred_lon) distances.append(dist) if timediff != 0: speeds.append(speed) else: speeds.append(10000.) #if all speeds exceed 40 knots then create new ship if min(speeds) > 40.0 / knots_conversion: n_ships = n_ships + 1 voy = ex.Voyage() voy.add_report(invoyage.getrep(i)) outvoyages.append(voy) result.append(n_ships) #else ob is assigned to ship whose predicted location is closest to the ob else: winner = distances.index(min(distances)) outvoyages[winner].add_report(invoyage.getrep(i)) result.append(winner+1) return outvoyages
def distr1(invoyage): ''' Given an object of :class:`.Voyage`, calculate what the distance is between the projected position (based on the reported speed and heading at the current and previous time steps) and the actual position. The observations are taken in time order. :param invoyage: Object of :class:`.Voyage` :type invoyage: :class:`.Voyage` :return: list of distances from estimated positions :rtype: list of floats This takes the speed and direction reported by the ship and projects it forwards half a time step, it then projects it forwards another half time step using the speed and direction for the next report, to which the projected location is then compared. The distances between the projected and actual locations is returned ''' #Compute difference between actual and expected positions after #two observations. #Each vessel travels at its reported speed and direction for #half the time interval; the difference (in miles) between #the calculated and observed positions is calculatered. km_to_nm = 0.539957 nobs = len(invoyage) distance_from_est_location = [None] for i in range(1, nobs): if (invoyage.getvar(i, 'vsi') != None and invoyage.getvar(i-1, 'vsi') != None and invoyage.getvar(i, 'dsi') != None and invoyage.getvar(i-1, 'dsi') != None and invoyage.getvar(i, 'time_diff') != None): #get increment from initial position lat1, lon1 = increment_position(invoyage.getvar(i-1, 'LAT'), invoyage.getvar(i-1, 'LON'), invoyage.getvar(i-1, 'vsi')/km_to_nm, invoyage.getvar(i-1, 'dsi'), invoyage.getvar(i, 'time_diff')) lat2, lon2 = increment_position(invoyage.getvar(i, 'LAT'), invoyage.getvar(i, 'LON'), invoyage.getvar(i, 'vsi')/km_to_nm, invoyage.getvar(i, 'dsi'), invoyage.getvar(i, 'time_diff')) #apply increments to the lat and lon at i-1 alatx = invoyage.getvar(i-1, 'LAT') + lat1 + lat2 alonx = invoyage.getvar(i-1, 'LON') + lon1 + lon2 #calculate distance between calculated position and the second reported position discrepancy = sph.sphere_distance(invoyage.getvar(i, 'LAT'), invoyage.getvar(i, 'LON'), alatx, alonx) distance_from_est_location.append(discrepancy) else: #in the absence of reported speed and direction set to None distance_from_est_location.append(None) return distance_from_est_location
def distr2(invoyage): ''' Given a list of :class:`.Voyage` , calculate what the distance is between the projected position (based on the reported speed and heading at the current and previous time steps) and the actual position. The calculation proceeds from the final, later observation to the first (in contrast to distr1 which runs in time order) :param invoyage: List of :class:`.Voyage` :type invoyage: :class:`.Voyage` :return: list of distances from estimated positions :rtype: list of floats This takes the speed and direction reported by the ship and projects it forwards half a time step, it then projects it forwards another half time step using the speed and direction for the next report, to which the projected location is then compared. The distances between the projeted and actual locations is returned ''' #Compute difference between actual and expected positions after #two observations IN REVERSE ORDER #Each vessel travels at its reported speed and direction for #half the time interval; the difference (in miles) between #the calculated and observed positions is calculatered. km_to_nm = 0.539957 nobs = len(invoyage) distance_from_est_location = [None] for i in range(nobs-1, 0, -1): if (invoyage.getvar(i, 'vsi') != None and invoyage.getvar(i-1, 'vsi') != None and invoyage.getvar(i, 'dsi') != None and invoyage.getvar(i-1, 'dsi') != None and invoyage.getvar(i, 'time_diff') != None): #get increment from initial position - backwards in time #means reversing the direction by 180 degrees lat1, lon1 = increment_position(invoyage.getvar(i, 'LAT'), invoyage.getvar(i, 'LON'), invoyage.getvar(i, 'vsi') / km_to_nm, invoyage.getvar(i, 'dsi') - 180., invoyage.getvar(i, 'time_diff')) lat2, lon2 = increment_position(invoyage.getvar(i-1, 'LAT'), invoyage.getvar(i-1, 'LON'), invoyage.getvar(i-1, 'vsi') / km_to_nm, invoyage.getvar(i-1, 'dsi') - 180., invoyage.getvar(i, 'time_diff')) #apply increments to the lat and lon at i-1 alatx = invoyage.getvar(i, 'LAT') + lat1 + lat2 alonx = invoyage.getvar(i, 'LON') + lon1 + lon2 #calculate distance between calculated position and the second reported position discrepancy = sph.sphere_distance(invoyage.getvar(i-1, 'LAT'), invoyage.getvar(i-1, 'LON'), alatx, alonx) distance_from_est_location.append(discrepancy) else: #in the absence of reported speed and direction set to None distance_from_est_location.append(None) #that fancy bit at the end reverses the array return distance_from_est_location[::-1]