kopia lustrzana https://github.com/glidernet/ogn-python
Check if airports available
rodzic
d9c8790e48
commit
42e1c13aa5
|
@ -3,7 +3,7 @@ from datetime import timedelta
|
|||
from celery.utils.log import get_task_logger
|
||||
|
||||
from sqlalchemy import and_, or_, insert, between, exists
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.sql import func, null
|
||||
from sqlalchemy.sql.expression import case
|
||||
|
||||
from ogn.collect.celery import app
|
||||
|
@ -12,29 +12,6 @@ from ogn.model import AircraftBeacon, TakeoffLanding, Airport
|
|||
logger = get_task_logger(__name__)
|
||||
|
||||
|
||||
def get_aircraft_beacon_start_id(session):
|
||||
# returns the last AircraftBeacon used for TakeoffLanding
|
||||
last_takeoff_landing_query = session.query(func.max(TakeoffLanding.id).label('max_id')) \
|
||||
.subquery()
|
||||
|
||||
last_used_aircraft_beacon_query = session.query(AircraftBeacon.id) \
|
||||
.filter(TakeoffLanding.id == last_takeoff_landing_query.c.max_id) \
|
||||
.filter(and_(AircraftBeacon.timestamp == TakeoffLanding.timestamp,
|
||||
AircraftBeacon.device_id == TakeoffLanding.device_id))
|
||||
|
||||
last_used_aircraft_beacon_id = last_used_aircraft_beacon_query.first()
|
||||
if last_used_aircraft_beacon_id is None:
|
||||
min_aircraft_beacon_id = session.query(func.min(AircraftBeacon.id)).first()
|
||||
if min_aircraft_beacon_id is None:
|
||||
start_id = 0
|
||||
else:
|
||||
start_id = min_aircraft_beacon_id[0]
|
||||
else:
|
||||
start_id = last_used_aircraft_beacon_id[0] + 1
|
||||
|
||||
return start_id
|
||||
|
||||
|
||||
@app.task
|
||||
def compute_takeoff_and_landing(session=None):
|
||||
logger.info("Compute takeoffs and landings.")
|
||||
|
@ -42,6 +19,12 @@ def compute_takeoff_and_landing(session=None):
|
|||
if session is None:
|
||||
session = app.session
|
||||
|
||||
# check if we have any airport
|
||||
airports_query = session.query(Airport)
|
||||
if not airports_query.all():
|
||||
logger.warn("Cannot calculate takeoff and landings without any airport! Please import airports first.")
|
||||
return
|
||||
|
||||
# takeoff / landing detection is based on 3 consecutive points
|
||||
takeoff_speed = 55 # takeoff detection: 1st point below, 2nd and 3rd above this limit
|
||||
landing_speed = 40 # landing detection: 1st point above, 2nd and 3rd below this limit
|
||||
|
@ -52,10 +35,6 @@ def compute_takeoff_and_landing(session=None):
|
|||
airport_radius = 0.025 # takeoff / landing must not exceed this radius (degree!) around the airport
|
||||
airport_delta = 100 # takeoff / landing must not exceed this altitude offset above/below the airport
|
||||
|
||||
# AircraftBeacon start id and end id
|
||||
aircraft_beacon_start_id = get_aircraft_beacon_start_id(session)
|
||||
aircraft_beacon_end_id = aircraft_beacon_start_id + 500000
|
||||
|
||||
# 'wo' is the window order for the sql window function
|
||||
wo = and_(AircraftBeacon.device_id,
|
||||
AircraftBeacon.timestamp,
|
||||
|
@ -82,53 +61,55 @@ def compute_takeoff_and_landing(session=None):
|
|||
AircraftBeacon.device_id,
|
||||
func.lag(AircraftBeacon.device_id).over(order_by=wo).label('device_id_prev'),
|
||||
func.lead(AircraftBeacon.device_id).over(order_by=wo).label('device_id_next')) \
|
||||
.filter(between(AircraftBeacon.id, aircraft_beacon_start_id, aircraft_beacon_end_id)) \
|
||||
.filter(AircraftBeacon.status == null()) \
|
||||
.subquery()
|
||||
|
||||
sq2 = session.query(sq) \
|
||||
.filter(sq.c.device_id_prev == sq.c.device_id == sq.c.device_id_next) \
|
||||
.subquery()
|
||||
|
||||
# find possible takeoffs and landings
|
||||
sq2 = session.query(
|
||||
sq.c.id,
|
||||
sq.c.timestamp,
|
||||
case([(sq.c.ground_speed > takeoff_speed, sq.c.location_wkt_prev), # on takeoff we take the location from the previous fix because it is nearer to the airport
|
||||
(sq.c.ground_speed < landing_speed, sq.c.location)]).label('location'),
|
||||
case([(sq.c.ground_speed > takeoff_speed, sq.c.track),
|
||||
(sq.c.ground_speed < landing_speed, sq.c.track_prev)]).label('track'), # on landing we take the track from the previous fix because gliders tend to leave the runway quickly
|
||||
sq.c.ground_speed,
|
||||
sq.c.altitude,
|
||||
case([(sq.c.ground_speed > takeoff_speed, True),
|
||||
(sq.c.ground_speed < landing_speed, False)]).label('is_takeoff'),
|
||||
sq.c.device_id) \
|
||||
.filter(sq.c.device_id_prev == sq.c.device_id == sq.c.device_id_next) \
|
||||
.filter(or_(and_(sq.c.ground_speed_prev < takeoff_speed, # takeoff
|
||||
sq.c.ground_speed > takeoff_speed,
|
||||
sq.c.ground_speed_next > takeoff_speed),
|
||||
and_(sq.c.ground_speed_prev > landing_speed, # landing
|
||||
sq.c.ground_speed < landing_speed,
|
||||
sq.c.ground_speed_next < landing_speed))) \
|
||||
.filter(sq.c.timestamp_next - sq.c.timestamp_prev < timedelta(seconds=duration)) \
|
||||
.filter(and_(func.ST_DFullyWithin(sq.c.location, sq.c.location_wkt_prev, radius),
|
||||
func.ST_DFullyWithin(sq.c.location, sq.c.location_wkt_next, radius))) \
|
||||
sq3 = session.query(
|
||||
sq2.c.id,
|
||||
sq2.c.timestamp,
|
||||
case([(sq2.c.ground_speed > takeoff_speed, sq2.c.location_wkt_prev), # on takeoff we take the location from the previous fix because it is nearer to the airport
|
||||
(sq2.c.ground_speed < landing_speed, sq2.c.location)]).label('location'),
|
||||
case([(sq2.c.ground_speed > takeoff_speed, sq2.c.track),
|
||||
(sq2.c.ground_speed < landing_speed, sq2.c.track_prev)]).label('track'), # on landing we take the track from the previous fix because gliders tend to leave the runway quickly
|
||||
sq2.c.ground_speed,
|
||||
sq2.c.altitude,
|
||||
case([(sq2.c.ground_speed > takeoff_speed, True),
|
||||
(sq2.c.ground_speed < landing_speed, False)]).label('is_takeoff'),
|
||||
sq2.c.device_id) \
|
||||
.filter(sq2.c.timestamp_next - sq2.c.timestamp_prev < timedelta(seconds=duration)) \
|
||||
.filter(and_(func.ST_DFullyWithin(sq2.c.location, sq2.c.location_wkt_prev, radius),
|
||||
func.ST_DFullyWithin(sq2.c.location, sq2.c.location_wkt_next, radius))) \
|
||||
.filter(or_(and_(sq2.c.ground_speed_prev < takeoff_speed, # takeoff
|
||||
sq2.c.ground_speed > takeoff_speed,
|
||||
sq2.c.ground_speed_next > takeoff_speed),
|
||||
and_(sq2.c.ground_speed_prev > landing_speed, # landing
|
||||
sq2.c.ground_speed < landing_speed,
|
||||
sq2.c.ground_speed_next < landing_speed))) \
|
||||
.subquery()
|
||||
|
||||
# consider them if they are near a airport
|
||||
sq3 = session.query(
|
||||
sq2.c.timestamp,
|
||||
sq2.c.track,
|
||||
sq2.c.is_takeoff,
|
||||
sq2.c.device_id,
|
||||
sq4 = session.query(
|
||||
sq3.c.timestamp,
|
||||
sq3.c.track,
|
||||
sq3.c.is_takeoff,
|
||||
sq3.c.device_id,
|
||||
Airport.id.label('airport_id')) \
|
||||
.filter(and_(func.ST_DFullyWithin(sq2.c.location, Airport.location_wkt, airport_radius),
|
||||
between(sq2.c.altitude, Airport.altitude - airport_delta, Airport.altitude + airport_delta))) \
|
||||
.filter(and_(func.ST_DFullyWithin(sq3.c.location, Airport.location_wkt, airport_radius),
|
||||
between(sq3.c.altitude, Airport.altitude - airport_delta, Airport.altitude + airport_delta))) \
|
||||
.filter(between(Airport.style, 2, 5)) \
|
||||
.order_by(sq2.c.id) \
|
||||
.subquery()
|
||||
|
||||
# consider them only if they are not already existing in db
|
||||
takeoff_landing_query = session.query(sq3) \
|
||||
takeoff_landing_query = session.query(sq4) \
|
||||
.filter(~exists().where(
|
||||
and_(TakeoffLanding.timestamp == sq3.c.timestamp,
|
||||
TakeoffLanding.device_id == sq3.c.device_id,
|
||||
TakeoffLanding.airport_id == sq3.c.airport_id)))
|
||||
and_(TakeoffLanding.timestamp == sq4.c.timestamp,
|
||||
TakeoffLanding.device_id == sq4.c.device_id,
|
||||
TakeoffLanding.airport_id == sq4.c.airport_id)))
|
||||
|
||||
# ... and save them
|
||||
ins = insert(TakeoffLanding).from_select((TakeoffLanding.timestamp,
|
||||
|
|
Ładowanie…
Reference in New Issue