def test_get_distance(self): coordinates_distance = [ ((50.355136, 7.566077), (50.353968, 4.577915), 212), ((-5.135943, -42.792442), (4.606085, 120.028077), 18130) ] for coord1, coord2, distance in coordinates_distance: assert int(utils.get_distance(coord1, coord2)) == distance
def appropriate_epsilon_km(self, px=5): """Determine an epsilon value appropriate for the current projection and figure size. The epsilon value gives the distance required in map projection coordinates that corresponds to approximately px Pixels in screen coordinates. The value can be used to find the line/point that is closest to a click while discarding clicks that are too far away from any geometry feature. """ # (bounds = left, bottom, width, height) ax_bounds = self.ax.bbox.bounds diagonal = math.hypot(round(ax_bounds[2]), round(ax_bounds[3])) if self.map.projection in ['stere', 'lcc']: map_delta = np.hypot(self.map.llcrnry - self.map.urcrnry, self.map.llcrnrx - self.map.urcrnrx) / 1000. else: map_delta = get_distance((self.map.llcrnry, self.map.llcrnrx), (self.map.urcrnry, self.map.urcrnrx)) km_per_px = map_delta / diagonal return km_per_px * px
def compute_solar_lines(self, bmap, wp_vertices, wp_heights, wp_times, solartype): """ Computes coloured overlay over the flight path that indicates the danger of looking into the sun with a limb sounder aboard the aircraft. Args: bmap: Projection of TopView wp_vertices: waypoints of the flight path wp_heights: altitude of the waypoints of flight path Returns: LineCollection of coloured lines according to the angular distance between viewing direction and solar angle """ # calculate distances and times body, difftype = solartype times = [datetime_to_jsec(_wp_time) for _wp_time in wp_times] x, y = list(zip(*wp_vertices)) wp_lons, wp_lats = bmap(x, y, inverse=True) fine_lines = [ bmap.gcpoints2(wp_lons[i], wp_lats[i], wp_lons[i + 1], wp_lats[i + 1], map_coords=False) for i in range(len(wp_lons) - 1) ] line_heights = [ np.linspace(wp_heights[i], wp_heights[i + 1], num=len(fine_lines[i][0])) for i in range(len(fine_lines)) ] line_times = [ np.linspace(times[i], times[i + 1], num=len(fine_lines[i][0])) for i in range(len(fine_lines)) ] # fine_lines = list of tuples with x-list and y-list for each segment # lines = list of tuples with lon-list and lat-list for each segment heights = [] times = [] for i in range(len(fine_lines) - 1): heights.extend(line_heights[i][:-1]) times.extend(line_times[i][:-1]) heights.extend(line_heights[-1]) times.extend(line_times[-1]) solar_x = [] solar_y = [] for i in range(len(fine_lines) - 1): solar_x.extend(fine_lines[i][0][:-1]) solar_y.extend(fine_lines[i][1][:-1]) solar_x.extend(fine_lines[-1][0]) solar_y.extend(fine_lines[-1][1]) points = [] old_wp = None total_distance = 0 for i, (lon, lat) in enumerate(zip(solar_x, solar_y)): points.append([[lon, lat] ]) # append double-list for later concatenation if old_wp is not None: wp_dist = get_distance((old_wp[0], old_wp[1]), (lat, lon)) * 1000. total_distance += wp_dist old_wp = (lat, lon) vals = [] for i in range(len(points) - 1): p0, p1 = points[i][0], points[i + 1][0] sol_azi, sol_ele = self.compute_body_angle(body, times[i], p0[0], p0[1]) obs_azi, obs_ele = self.compute_view_angles( p0[0], p0[1], heights[i], p1[0], p1[1], heights[i + 1], self.dsbObsAngleAzimuth.value(), self.dsbObsAngleElevation.value()) if sol_azi < 0: sol_azi += 360 if obs_azi < 0: obs_azi += 360 rating = self.calc_view_rating(obs_azi, obs_ele, sol_azi, sol_ele, heights[i], difftype) vals.append(rating) # convert lon, lat to map points for i in range(len(points)): points[i][0][0], points[i][0][1] = bmap(points[i][0][0], points[i][0][1]) points = np.concatenate([points[:-1], points[1:]], axis=1) # plot solar_lines = LineCollection(points, cmap=self.solar_cmap, norm=self.solar_norm, zorder=2, linewidths=3, animated=True) solar_lines.set_array(np.array(vals)) return solar_lines
def update_distances(self, position, rows=1): """Update all distances in a flight track that are affected by a waypoint change involving <rows> waypoints starting at index <position>. Distances are computed along great circles. If rows=1, the distance to the previous waypoint is updated for waypoints <position> and <position+1>. The total flight track distance is updated for all waypoint following <position>. If rows>1, the distances to the previous waypoints are updated according to the number of modified waypoints. """ waypoints = self.waypoints aircraft = self.performance_settings["aircraft"] def get_duration_fuel(flightlevel0, flightlevel1, distance, weight, lastleg): if flightlevel0 == flightlevel1: tas, fuelflow = aircraft.get_cruise_performance( flightlevel0 * 100, weight) duration = 3600. * distance / ( 1.852 * tas) # convert to s (tas is in nm/h) leg_fuel = duration * fuelflow / 3600. return duration, leg_fuel else: if flightlevel0 < flightlevel1: duration0, dist0, fuel0 = aircraft.get_climb_performance( flightlevel0 * 100, weight) duration1, dist1, fuel1 = aircraft.get_climb_performance( flightlevel1 * 100, weight) else: duration0, dist0, fuel0 = aircraft.get_descent_performance( flightlevel0 * 100, weight) duration1, dist1, fuel1 = aircraft.get_descent_performance( flightlevel1 * 100, weight) duration = (duration1 - duration0) * 60 # convert from min to s dist = (dist1 - dist0) * 1.852 # convert from nm to km fuel = fuel1 - fuel0 if lastleg: duration_p, fuel_p = get_duration_fuel( flightlevel0, flightlevel0, distance - dist, weight, False) else: duration_p, fuel_p = get_duration_fuel( flightlevel1, flightlevel1, distance - dist, weight, False) return duration + duration_p, fuel + fuel_p pos = position for offset in range(rows): pos = position + offset wp1 = waypoints[pos] # The distance to the first waypoint is zero. if pos == 0: wp1.distance_to_prev = 0. wp1.distance_total = 0. wp1.leg_time = 0 # time from previous waypoint wp1.cum_time = 0 # total time of flight wp1.utc_time = self.performance_settings[ "takeoff_time"].toPyDateTime() wp1.weight = self.performance_settings["takeoff_weight"] wp1.leg_fuel = 0 wp1.rem_fuel = self.performance_settings["fuel"] wp1.ascent_rate = 0 else: wp0 = waypoints[pos - 1] wp1.distance_to_prev = utils.get_distance((wp0.lat, wp0.lon), (wp1.lat, wp1.lon)) last = (pos - 1 == rows) time, fuel = get_duration_fuel(wp0.flightlevel, wp1.flightlevel, wp1.distance_to_prev, wp0.weight, lastleg=last) wp1.leg_time = time wp1.cum_time = wp0.cum_time + wp1.leg_time wp1.utc_time = wp0.utc_time + datetime.timedelta( seconds=wp1.leg_time) wp1.leg_fuel = fuel wp1.rem_fuel = wp0.rem_fuel - wp1.leg_fuel wp1.weight = wp0.weight - wp1.leg_fuel if wp1.leg_time != 0: wp1.ascent_rate = int((wp1.flightlevel - wp0.flightlevel) * 100 / (wp1.leg_time / 60)) else: wp1.ascent_rate = 0 wp1.ceiling_alt = aircraft.get_ceiling_altitude(wp1.weight) # Update the distance of the following waypoint as well. if pos < len(waypoints) - 1: wp2 = waypoints[pos + 1] wp2.distance_to_prev = utils.get_distance((wp1.lat, wp1.lon), (wp2.lat, wp2.lon)) if wp2.leg_time != 0: wp2.ascent_rate = int((wp2.flightlevel - wp1.flightlevel) * 100 / (wp2.leg_time / 60)) else: wp2.ascent_rate = 0 # Update total distances of waypoint at index position and all # following waypoints. for i in range(max(min(position, 1), 1), len(waypoints)): wp0 = waypoints[i - 1] wp1 = waypoints[i] wp1.distance_total = wp0.distance_total + wp1.distance_to_prev wp1.weight = wp0.weight - wp0.leg_fuel last = (i + 1 == len(waypoints)) time, fuel = get_duration_fuel(wp0.flightlevel, wp1.flightlevel, wp1.distance_to_prev, wp0.weight, lastleg=last) wp1.leg_time = time wp1.cum_time = wp0.cum_time + wp1.leg_time wp1.utc_time = wp0.utc_time + datetime.timedelta( seconds=wp1.leg_time) wp1.leg_fuel = fuel wp1.rem_fuel = wp0.rem_fuel - wp1.leg_fuel wp1.weight = wp0.weight - wp1.leg_fuel wp1.ceiling_alt = aircraft.get_ceiling_altitude(wp1.weight) index1 = self.createIndex(0, TIME_UTC) self.dataChanged.emit(index1, index1)