Metadata-Version: 2.4
Name: plane-above
Version: 0.1.0
Summary: What’s flying up there, huh?
Author: Ilia Andreevich
License-Expression: MIT
Project-URL: Homepage, https://github.com/wrongcredentials/plane-above/
Project-URL: Repository, https://github.com/wrongcredentials/plane-above
Project-URL: Issues, https://github.com/wrongcredentials/plane-above/issues
Keywords: aviation,aircraft,planes,flight-tracking,air-traffic,opensky
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.28.0
Requires-Dist: lxml>=5.1.0
Requires-Dist: python-dateutil>=2.9.0
Requires-Dist: country-converter>=1.3.0
Dynamic: license-file

<img src="logo.svg" alt="plane-above" width="200">

# Plane Above
Plane Above is a python package featuring a convinient way to retrieve planes above certain point.

<details>
  <summary>One notable usage example...</summary>

  Try <a href="https://t.me/plane_above_bot">Plane Above</a> telegram bot.
  <img src="tg.png" alt="plane-above-bot" width="400">
</details>

## Before we start...
To collect data, this library utilizes various sources. They are open and maintained by enthusiasts mostly.
So, use responsively! Don't push the limits and take actions with care.
Please mind that data might be missing, incorrect or outdated.

## How it works
_Note: here and after I would refer to any flying object as an «aircraft» or «plane»,
but keep in mind that it also includes helicopters, gliders, rotorcraft and even balloons._
#### 1. BUILD SEARCHING AREA
With given latitude and longitude of point  it builds area of search by calculating bounding box coordinates
based on required distance from point.
#### 2. RETRIEVE STATES
Next it makes a request to OpenSky API to retrieve all states in searching area.
Each state represents [state vector](https://openskynetwork.github.io/opensky-api/index.html#state-vectors)
and contains information of ADS-B messages from transponder installed in aircraft.
#### 3. PICK DATA
After receiving states (if request was successful, we did not exceed limits and received at least one state)
it picks crucial data and filters out excessive entries: for instance objects that are on ground will be discarded.
At this point we already have some valuable data such as transponder hex code and flight callsign,
along with registered altitude and velocity. 
#### 4. FETCH DETAILS
Based on previous step we enrich the data by making requests to multiple sources to retrieve information about aircraft
such as: registration code, manufacturer and model, age (based on built date) and operating airline/registered owner.
It continues with the route and send multiple requests as well to receive information about departure,
destination and for some cases midpoint airports. Finally, it searches for the aircraft photo.
#### 5. BUILD RESULT
This is the last step where all data being returned in declared format.

## Installation
```bash
pip install plane-above
```


## Usage
First, import library, create instance and pass your values of latitude & longitude as a tuple:
```python
>>> from plane_above import PlaneAbove
>>> pa = PlaneAbove((52.4573212, 5.5301535))
```
Optionally, you can pass a distance attribute (in km, 15 by default):
```python
>>> pa = PlaneAbove((52.4573212, 5.5301535), distance=20)
```
Great, by this time we already know what is happening above that point, so let's check it:
```python
>>> pa.spotted.success
True
>>> pa.spotted.how_many
2
```
In cases when there are no plane detected, or we failed to retrieve information you will get
```True, 0``` and ```False, 0``` respectively. Let's go further and see what are that 2 flying objects:
```python
>>> for plane in pa.fetch():
...     print(f"{plane.aircraft.manufacturer} {plane.aircraft.model}
...     flying to {plane.route.destination.name} is {plane.state.altitude} meters above you!")

Boeing 777 212ER flying to London Heathrow Airport is 11232 meters above you!
Airbus A330 342 flying to Brussels Airport (Zaventem Airport) is 1006 meters above you!
```
If you live in acynchronious environment, call ```async.fetch()``` instead:
```python 
>>> async for plane in pa.async_fetch():
    ...
```

## Object Reference
<details>
  <summary>Aircraft</summary>

  ```python
class Aircraft:
    registration: str
    manufacturer: str
    model: str
    operator: str
    age: float
  ```
  Note: don’t take age 0.0 as a really new plane, as age is might be missing from our sources;
  all strings might be empty strings.
</details>

<details>
  <summary>Photo</summary>

  ```python
class Photo:
    image_url: str
    origin_url: str
    photographer: str
  ```
  Note: all strings might be empty strings.
</details>

<details>
  <summary>Airport</summary>

  ```python
class Airport:
    iata: str = "N/A"
    name: str = "Unknown airport"
    country_code: str = ""
  ```
</details>

<details>
  <summary>Route</summary>

  ```python
class Route:
    departure: Airport
    destination: Airport
    stops: list[Airport]
    airline: str | None = None
  ```
</details>

<details>
  <summary>State</summary>

  ```python
class State:
    altitude: int
    velocity: int
  ```
  Note: velocity and/or altitude can be 0 if no data from transponder received.
</details>

<details>
  <summary>Plane</summary>

  ```python
class Plane:
    icao24: str
    callsign: str
    country_code: str
    aircraft: Aircraft
    route: Route
    state: State
    photo: Photo
  ```
</details>

## Limitations
OpenSky Network allows to use it's api anonymously, but with limits.
If you wish to extend your usage - consider obtaining ```client_id``` and  ```client_secret```
[here](https://openskynetwork.github.io/opensky-api/rest.html#authentication). Then you pass it like this:
```python
>>> pa = PlaneAbove((52.4573212, 5.5301535), osn_id="your_client_id", osn_secret="your_client_secret")
```

In some cases your calls to OpenSky Network might be timed-out without any specific reason, that's due to OSN
blocking policies and you probably would like to utilize proxy here as a workaround:
```python
>>> pa = PlaneAbove((52.4573212, 5.5301535), osn_proxy="http://username:password@host:port")
```
Please note that this will be used for making an OSN request only; other sources won't be called with that proxy.

One of our photo sources has a strict policy about making requests with 
[unique and descriptive user-agents](https://www.planespotters.net/photo/api). That's doable with an extra param:
```python
>>> pa = PlaneAbove((52.4573212, 5.5301535), ps_user_agent="YourApp/1.0 (+https://example.com/contact)")
```
However, you are free to skip it as it won't affect other photo sources.

## Credits
* Basic and essential: https://opensky-network.org/
* Aircraft data & photo, route & airport details: https://hexdb.io/
* Aircraft data, route & airport details: https://adsbdb.com/
* Aircraft photo, airport details: https://airport-data.com/
* Aircraft data: https://flightdb.net/ 
* Aircraft photo: https://planespotters.net/
