Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial tracks support #4

Merged
merged 7 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 159 additions & 54 deletions alaqs_core/interfaces/Movement.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
import numpy as np
import pandas as pd
from PyQt5 import QtCore, QtWidgets
from shapely.geometry import MultiLineString
from shapely import ops
from shapely.geometry import LineString, MultiLineString
from shapely.wkt import loads
from inspect import getframeinfo, currentframe

from open_alaqs.alaqs_core import alaqs
from open_alaqs.alaqs_core.alaqslogging import get_logger
from open_alaqs.alaqs_core.interfaces.Aircraft import AircraftStore
from open_alaqs.alaqs_core.interfaces.AircraftTrajectory import \
Expand All @@ -20,6 +22,7 @@
HeliEngineStore
from open_alaqs.alaqs_core.interfaces.Gate import GateStore
from open_alaqs.alaqs_core.interfaces.Runway import RunwayStore
from open_alaqs.alaqs_core.interfaces.Track import TrackStore
from open_alaqs.alaqs_core.interfaces.SQLSerializable import SQLSerializable
from open_alaqs.alaqs_core.interfaces.Store import Store
from open_alaqs.alaqs_core.interfaces.Taxiway import TaxiwayRoutesStore
Expand Down Expand Up @@ -148,6 +151,7 @@ def __init__(self, val=None):
self._runway = None
self._trajectory_cartesian = None
self._trajectory_at_runway = None
self._track = None

def getAPUCode(self):
return self._apu_code
Expand Down Expand Up @@ -435,7 +439,7 @@ def calculateTaxiingEmissions(self, method=None, mode="TX", sas='none'):
# load APU time and emission factors
apu_t, apu_em = self.loadAPUinfo(index_segment_)
apu_time = 0
if (apu_t is not None) and (apu_t > 0) :
if (apu_t is not None and apu_em is not None) and (apu_t > 0) :
# APU emissions will be added to the stand only
if self.getAPUCode() == 1 and index_segment_ == 0:
apu_time = apu_t
Expand Down Expand Up @@ -604,7 +608,7 @@ def calculateFlightEmissions(
distance_time_all_segments_in_mode = 0.
distance_space_all_segments_in_mode = 0.
traj = self.getTrajectoryAtRunway() if atRunway else self.getTrajectory()

if traj is None:
return emissions

Expand Down Expand Up @@ -1026,10 +1030,6 @@ def calculateTrajectoryAtRunway(self, offset_by_touchdown=True):
f"time='{self.getRunwayTime(as_str=True)}'.")
else:

trajectory = AircraftTrajectory(
self.getTrajectory(), skipPointInitialization=True)
trajectory.setIsCartesian(False)

# Shift coordinates by touchdown offset (only for arrivals)
if offset_by_touchdown:
offset_by_touchdown = self.isArrival()
Expand Down Expand Up @@ -1115,48 +1115,130 @@ def calculateTrajectoryAtRunway(self, offset_by_touchdown=True):

# DEP: construction starts with nearest point
# ARR: construction starts with point most far away
for point in self.getTrajectory().getPoints():

# aircraft trajectory point with from default_aircraft_profiles
origin = (0., 0., 0.)

# target point with cartesian coordinates
target_point = point.getCoordinates()
target_point_distance = spatial.getDistanceXY(
target_point[0], target_point[1], target_point[2],
origin[0], origin[1], origin[2])

# if self.getDepartureArrivalFlag() == "A":
# target_point_distance = target_point_distance + self.getTrajectory().getPoints()[-1].getX()
# if self.getDepartureArrivalFlag() == "A":
# if self.getRunway().getDirections().index(self.getRunwayDirection()):
# target_point_distance = target_point_distance - self.getTrajectory().getPoints()[-1].getX()
# if target_point == origin:
# # for ARR, when landing take forward azimuth
# runway_azimuth = runway_azimuth + 180 if runway_azimuth < 180 else runway_azimuth - 180
# else:
# target_point_distance = target_point_distance - self.getTrajectory().getPoints()[-1].getX()
# if target_point == origin:
# # for ARR, when landing take forward azimuth
# runway_azimuth = runway_azimuth + 180 if runway_azimuth < 180 else runway_azimuth - 180

# get target point (calculation in 4326 projection)
target_projected = spatial.getDistance(runway_point[1], runway_point[0], runway_azimuth, target_point_distance)

# target point (wkt) with coordinates in 4326
target_projected_wkt = spatial.getPointGeometryText(target_projected["lon2"], target_projected["lat2"], 0., swap)

# reproject target from 4326 to 3857
(target_projected_wkt, swap_) = spatial.reproject_geometry(target_projected_wkt, epsg_id_target, epsg_id_source)

# add target to list of points of the (shifted) trajectory
for p in spatial.getAllPoints(target_projected_wkt):
p_ = AircraftTrajectoryPoint(point)
# Update x and y coordinates (z coordinate is not updated by distance calculation)
p_.setCoordinates(p[0], p[1], target_point[2])
p_.updateGeometryText()
trajectory.addPoint(p_)

has_track = self.getTrack() is not None
if has_track and self.getTaxiRoute().getRunway() != self.getTrack().getRunway():
logger.warning("Paired taxi route '%s' and track '%s' do not share the same runway, reverting movement to default airplane profile" % (self.getTaxiRoute().getName(), self.getTrack().getName()))
has_track = False
if has_track and self.getDepartureArrivalFlag() != self.getTrack().getDepartureArrivalFlag():
logger.warning("Track '%s' departure/arrival flag does not match movement, using default airplane profile instead" % (self.getTrack().getName()))
has_track = False

if not has_track:
# no track, create straight line from profile

trajectory = AircraftTrajectory(
self.getTrajectory(), skipPointInitialization=True)
trajectory.setIsCartesian(False)

for point in self.getTrajectory().getPoints():

# aircraft trajectory point with from default_aircraft_profiles
origin = (0., 0., 0.)

# target point with cartesian coordinates
target_point = point.getCoordinates()
target_point_distance = spatial.getDistanceXY(
target_point[0], target_point[1], target_point[2],
origin[0], origin[1], origin[2])

# if self.getDepartureArrivalFlag() == "A":
# target_point_distance = target_point_distance + self.getTrajectory().getPoints()[-1].getX()
# if self.getDepartureArrivalFlag() == "A":
# if self.getRunway().getDirections().index(self.getRunwayDirection()):
# target_point_distance = target_point_distance - self.getTrajectory().getPoints()[-1].getX()
# if target_point == origin:
# # for ARR, when landing take forward azimuth
# runway_azimuth = runway_azimuth + 180 if runway_azimuth < 180 else runway_azimuth - 180
# else:
# target_point_distance = target_point_distance - self.getTrajectory().getPoints()[-1].getX()
# if target_point == origin:
# # for ARR, when landing take forward azimuth
# runway_azimuth = runway_azimuth + 180 if runway_azimuth < 180 else runway_azimuth - 180

# get target point (calculation in 4326 projection)
target_projected = spatial.getDistance(runway_point[1], runway_point[0], runway_azimuth, target_point_distance)

# target point (wkt) with coordinates in 4326
target_projected_wkt = spatial.getPointGeometryText(target_projected["lon2"], target_projected["lat2"], 0., swap)

# reproject target from 4326 to 3857
(target_projected_wkt, swap_) = spatial.reproject_geometry(target_projected_wkt, epsg_id_target, epsg_id_source)

# add target to list of points of the (shifted) trajectory
for p in spatial.getAllPoints(target_projected_wkt):
p_ = AircraftTrajectoryPoint(point)
# Update x and y coordinates (z coordinate is not updated by distance calculation)
p_.setCoordinates(p[0], p[1], target_point[2])
p_.updateGeometryText()
trajectory.addPoint(p_)
trajectory.updateGeometryText()
else:
# process track

# build distance to point array from aircraft profile
profile_points = self.getTrajectory().getPoints()
profile_distances = []
previous_point = (0., 0., 0.)
cumulative_distance = 0.
for point in profile_points:
point = point.getCoordinates()
distance = spatial.getDistanceBetweenPoints(
point[0], point[1], point[2],
previous_point[0], previous_point[1], previous_point[2])
cumulative_distance = cumulative_distance + distance
profile_distances.append(cumulative_distance)
previous_point = point

difference = self.getTrack().getGeometry().difference(self.getRunway().getGeometry().buffer(10))
track_line = difference
max_length = 0.0
# check if the track has been broken into multipe parts, pick the longest one
if difference.geom_type is 'MultiLineString':
for line in list(difference.geoms):
if line.length > max_length:
max_length = line.length
track_line = line

track_line_points = list(track_line.coords)
if self.getTrack().getDepartureArrivalFlag() == 'A':
# reverse arrival track so ordering begins at runway
track_line_points.reverse()

(point, point_wkt) = spatial.reproject_Point(
runway_point[1], runway_point[0], epsg_id_target, epsg_id_source)
track_line_points.insert(0, (point.GetX(), point.GetY(), 0))
track_line = LineString(track_line_points)

trajectory = AircraftTrajectory()
trajectory.setIdentifier(self.getTrajectory().getIdentifier())
trajectory.setStage(self.getTrajectory().getStage())
trajectory.setSource(self.getTrajectory().getSource())
trajectory.setDepartureArrivalFlag(self.getTrajectory().getDepartureArrivalFlag())
trajectory.setWeight(self.getTrajectory().getWeight())

# match track points to closest point from the profile trajectory
previous_point = list(track_line.coords)[0]
cumulative_distance = 0.
for point in list(track_line.coords):
distance = spatial.getDistanceBetweenPoints(
point[0], point[1], point[2],
previous_point[0], previous_point[1], previous_point[2])
cumulative_distance = cumulative_distance + distance

closest_distance = profile_distances[-1]
closest_idx = len(profile_distances) - 1
for idx, d in enumerate(profile_distances):
if abs(distance - d) < closest_distance:
closest_distance = abs(distance - d)
closest_idx = idx

trajectory_point = AircraftTrajectoryPoint(profile_points[closest_idx])
trajectory_point.setCoordinates(point[0], point[1], point[2])
trajectory_point.updateGeometryText()
trajectory.addPoint(trajectory_point)
trajectory.updateGeometryText()

else:
logger.error("Did not find enough points for geometry '%s'" % (
runway_geometry_wkt))
Expand All @@ -1169,6 +1251,12 @@ def getRunway(self):
def setRunway(self, var):
self._runway = var

def setTrack(self, var):
self._track = var

def getTrack(self):
return self._track

# ["08R", "26L"]
def getRunwayDirection(self):
return self._runway_direction
Expand Down Expand Up @@ -1321,6 +1409,9 @@ def getGateStore(self):
def getTaxiRouteStore(self):
return TaxiwayRoutesStore(self._db_path)

def getTrackStore(self):
return TrackStore(self._db_path)

def ProgressBarWidget(self):
# from PyQt4 import QtGui, QtCore

Expand Down Expand Up @@ -1352,7 +1443,7 @@ def initMovements(self, debug=False):

df_cols = ["aircraft", "engine_name", "runway", "runway_direction",
"gate", "taxi_route", "profile_id", "trajectory",
"runway_trajectory"]
"runway_trajectory", "track_id"]
eq_mdf = pd.DataFrame(index=mdf.index, columns=df_cols)
eq_mdf = eq_mdf.fillna(np.nan) # fill with None rather than NaNs

Expand Down Expand Up @@ -1469,12 +1560,23 @@ def initMovements(self, debug=False):
if len(alt_routes) > 0:
eq_mdf.loc[indices, "taxi_route"] = alt_routes[0]
logger.warning("Taxiroute '%s' was replaced with '%s'",
(txr, alt_routes[0]))
txr, alt_routes[0])
else:
logger.error("No taxiroute found to replace '%s' "
"which is not in the database", txr)
eq_mdf.loc[indices, "taxi_route"] = np.NaN

# Check if track exist in the database
stage_1.nextValue()

track_store = self.getTrackStore()
for trk in mdf["track_id"].unique():
store_has_key = track_store.hasKey(trk)
eq_mdf.loc[mdf.track_id == trk, "track_id"] = \
trk if store_has_key else ''
if not store_has_key:
logger.warning("Track %s wasn't found in the DB" % trk)

# Check if profiles exist in the database
stage_1.nextValue()

Expand Down Expand Up @@ -1518,7 +1620,7 @@ def initMovements(self, debug=False):
eq_mdf.loc[indices, "profile_id"] = prf

# Get unique combinations of eq_mdf
u_columns = ["runway", "runway_direction", "taxi_route", "profile_id"]
u_columns = ["runway", "runway_direction", "taxi_route", "profile_id", "track_id"]
heli_engine_store = self.getHeliEngineStore()
engine_store = self.getEngineStore()

Expand All @@ -1529,8 +1631,7 @@ def initMovements(self, debug=False):
f'(n={stage_1._max - stage_1._min}) '
f'in {stage_1._end_time - stage_1._start_time}')

for (rwy, rwy_dir, tx_route, prf_id), mov_df in eq_mdf.groupby(u_columns):

for (rwy, rwy_dir, tx_route, prf_id, trk_id), mov_df in eq_mdf.groupby(u_columns):
# Get the indices
inds = mov_df.index

Expand All @@ -1550,6 +1651,7 @@ def initMovements(self, debug=False):
fm_runway = runway_store.getObject(fm["runway"])
fm_taxi_route = taxi_route_store.getObject(fm["taxi_route"])
fm_trajectory = trajectory_store.getObject(fm["profile_id"])
fm_track = track_store.getObject(fm["track_id"])
fm_engine = None
if engine_store.hasKey(fm["engine_name"]):
fm_engine = engine_store.getObject(fm["engine_name"])
Expand All @@ -1562,13 +1664,15 @@ def initMovements(self, debug=False):
proxy_mov.setAircraftEngine(fm_engine)
proxy_mov.setRunway(fm_runway)
proxy_mov.setRunwayDirection(fm["runway_direction"])
proxy_mov.setTrack(fm_track)
proxy_mov.setTaxiRoute(fm_taxi_route)
proxy_mov.setTrajectory(fm_trajectory)
proxy_mov.updateTrajectoryAtRunway()

# Update the dataframe
eq_mdf.loc[inds, "runway"] = proxy_mov.getRunway()
eq_mdf.loc[inds, "taxi_route"] = proxy_mov.getTaxiRoute()
eq_mdf.loc[inds, "track"] = proxy_mov.getTrack()
eq_mdf.loc[inds, "trajectory"] = proxy_mov.getTrajectory()
eq_mdf.loc[inds, "runway_trajectory"] = \
proxy_mov.getTrajectoryAtRunway()
Expand Down Expand Up @@ -1631,6 +1735,7 @@ def initMovements(self, debug=False):
mov.setRunway(mov_df_entry["runway"])
mov.setRunwayDirection(mov_df_entry["runway_direction"])
mov.setTaxiRoute(mov_df_entry["taxi_route"])
mov.setTrack(mov_df_entry["track"])
mov.setTrajectory(mov_df_entry["trajectory"])
mov.setTrajectoryAtRunway(mov_df_entry["runway_trajectory"])

Expand Down
Loading