lunes, 1 de abril de 2024

¿Donde está la Estación Espacial Internacional?

 Basado en el proyecto de la rapsberry pi org ¿Where is the Space Station?

Introducción.


En este proyecto vamos a usar un servicio web para encontrar la localización actual de la Estación Espacial Internacional o ISS por sus siglas en inglés. Con esos datos mostraremos su posición en un mapa.


Instrucciones.


El icono de la Estación Espacial Internacional aparecerá en el mapa mostrando donde está la ISS actualmente. El texto en amarillo nos indicará las personas que actualmente están en el espacio.


¿Quién está en el espacio?


Para saber el número de astronautas que están en la estación espacial internacional, vamos a usar un servicio web que nos va a proporcionar esa información y más.

Un servicio web tiene una dirección web (URL) al igual que muchas de las páginas web que visitas habitualmente. Lo único que en vez de devolvernos una página HTML  lo que nos devuelve son datos.

Prueba a entrar en la siguiente dirección web http://open-notify.org/Open-Notify-API/. Aquí encontraremos toda la información que necesitaremos. Esta página es de uso libre y nos la facilita y mantiene Nathan Bergey.

Comencemos buscando información sobre los astronautas que están en la estación. 

Abre está dirección en el navegador. http://api.open-notify.org/astros.json

Si todo ha ido bien deberías ver algo como esto:

{
  "message": "success",
  "people": [
    {
      "name": "Jasmin Moghbeli",
      "craft": "ISS"
    },
    {
      "name": "Andreas Mogensen",
      "craft": "ISS"
    },
    {
      "name": "Satoshi Furukawa",
      "craft": "ISS"
    },
    {
      "name": "Konstantin Borisov",
      "craft": "ISS"
    },
    {
      "name": "Oleg Kononenko",
      "craft": "ISS"
    },
    {
      "name": "Nikolai Chub",
      "craft": "ISS"
    },
    {
      "name": "Loral O'Hara",
      "craft": "ISS"
    }
  ],
  "number": 7
}

Los datos cambian continuamente, así que seguramente si estás consultando este post verás algo diferente. El formato en el que recibimos los datos se denomina "JSON". Se parecen bastante a los diccionario en Python, aunque ojo que no son iguales.

Vamos a construir un script de Python para poder usar esos datos.

Empieza creando un archivo de python y llámalo como quieras.

Lo primero que haremos es importar las librerías que necesitaremos para nuestro proyecto que son urllib.request, json y turtle

Ahora copia el siguiente código:

""" ¿Donde está la Estación Espacial Internacional? """

import urllib.request
import json
import turtle

# http://open-notify.org/Open-Notify-API/
url = 'http://api.open-notify.org/astros.json'
respuesta = urllib.request.urlopen(url)
astronautas = json.loads(respuesta.read())
print(astronautas)

Salida:

{'message': 'success', 'people': [{'name': 'Jasmin Moghbeli', 'craft': 'ISS'}, {'name': 'Andreas Mogensen', 'craft': 'ISS'}, {'name': 'Satoshi Furukawa', 'craft': 'ISS'}, {'name': 'Konstantin Borisov', 'craft': 'ISS'}, {'name': 'Oleg Kononenko', 'craft': 'ISS'}, {'name': 'Nikolai Chub', 'craft': 'ISS'}, {'name': "Loral O'Hara", 'craft': 'ISS'}], 'number': 7}


Este código utiliza la API (servicio web) de Open Notify para obtener información sobre la Estación Espacial Internacional (ISS, por sus siglas en inglés) y los astronautas que se encuentran en ella en ese momento. Vamos a ver como funciona.

1. `import urllib.request`: Importa el módulo `urllib.request`, que se utiliza para abrir y leer URL.

2. `import json`: Importa el módulo `json`, que se utiliza para trabajar con datos en formato JSON (JavaScript Object Notation).

3. `import turtle`: Importa el módulo `turtle`, que se utiliza para crear gráficos mediante programación.

4. `url = 'http://api.open-notify.org/astros.json'`: Define la URL de la API de Open Notify que proporciona información sobre los astronautas que se encuentran en la Estación Espacial Internacional en ese momento.

5. `respuesta = urllib.request.urlopen(url)`: Abre la URL definida anteriormente y almacena la respuesta en la variable `respuesta`. Es un objeto de Python.

6. `astronautas = json.loads(respuesta.read())`: Lee el contenido de la respuesta HTTP y lo carga como un objeto Python utilizando la función `loads` del módulo `json`. Esto convierte los datos JSON en un diccionario de Python, que se almacena en la variable `astronautas`.

7. `print(astronautas)`: Imprime en la consola el diccionario de Python que contiene la información sobre los astronautas en la ISS.

El diccionario astronautas, como puedes ver, tiene tres claves: message, people y number.

La clave message tiene el valor success lo que nos está diciendo que accedimos correctamente a la información del servicio web. Ten en cuenta que tu verás diferentes valores para la clave people y number dependiendo de quien esté actualmente en el espacio.

Para que la información sea más clara, ignoraremos los nombres de los astronautas de momento y nos centraremos solo en su número. Cambia la función print por:

print(f"Número actual de astronautas: {astronautas['number']}")

Para que los nombres de los astronautas sean más legibles utilizaremos un bucle for para recorrer el diccionario.

""" ¿Donde está la Estación Espacial Internacional? """

import urllib.request
import json
import turtle

# http://open-notify.org/Open-Notify-API/
url = 'http://api.open-notify.org/astros.json'
respuesta = urllib.request.urlopen(url)
astronautas = json.loads(respuesta.read())

print(f"Número actual de astronautas: {astronautas['number']}")
for persona in astronautas['people']:
    print(persona['name'])
Si lo ejecutas verás una salida parecida a esta:

Número actual de astronautas: 7
Jasmin Moghbeli
Andreas Mogensen
Satoshi Furukawa
Konstantin Borisov
Oleg Kononenko
Nikolai Chub
Loral O'Hara

Además de los nombres de los astronautas, la API o servicio web también nos proporciona información sobre la nave en la que están. ¿Sabrías modificar el código para que se muestre esto?

Por ejemplo:

Número actual de astronautas: 7
Jasmin Moghbeli está en ISS
Andreas Mogensen está en ISS
Satoshi Furukawa está en ISS
Konstantin Borisov está en ISS
Oleg Kononenko está en ISS
Nikolai Chub está en ISS
Loral O'Hara está en ISS

¿Dónde está la ISS?


La Estación Espacial Internacional orbita alrededor de la tierra. Completa una orbita a la misma aproximadamente cada hora y media, ya que viaja a una velocidad aproximada de 7,66 km por segundo. Bastante rápido ¿Verdad?

Vamos a volver a usar la API para saber las cordenadas de la ISS. Lo primero es abir el siguiente link en el navegador http://open-notify.org/Open-Notify-API/ISS-Location-Now/. Ahí encontraras la información que necesitas. 

Si ahora introduces esta dirección en el navegador http://api.open-notify.org/iss-now.json verás algo parecido a esto:

{
  "iss_position": {
    "longitude": "55.8408",
    "latitude": "4.2601"
  },
  "message": "success",
  "timestamp": 1711653120
}
El resultado contiene las coordenadas de la tierra sobre la que actualmente está la ISS. Recordemos:

- La latitud indica la posición sobre el eje norte-sur y puede tener valores entre 90 y -90 grados, estando el cero en el ecuador.
- La longitud indica la posición sobre el eje este-oeste y puede tomar valores entre 180 y -180 grados. El cero marca el primer meridiano que es el que atraviesa Greenwich en londres (UK)

Las coordenadas normalmente se dan como (latitud, longitud). En esta web https://www.latlong.net/ puedes encontrar las coordenadas de tu lugar de residencia o de cualquier otro. 

Necesitamos llamar al mismo servicio web desde Python para que nos facilite las coordenadas que necesitamos. Añade el siguiente código al programa, es muy parecido a lo que hicimos antes:

#...
url = 'http://api.open-notify.org/iss-now.json'
respuesta = urllib.request.urlopen(url)
iss_posicion = json.loads(respuesta.read())
print(iss_posicion)
Verás unos datos similares a estos:

{'iss_position': {'longitude': '67.5407', 'latitude': '-36.2401'}, 
'message': 'success', 
'timestamp': 1711659534}
Crearemos unas variables para guardar la latitud y la longitud de la nave. Reorganicemos el código, el programa será algo como esto:

""" ¿Donde está la Estación Espacial Internacional? """

import urllib.request
import json
import turtle

# http://open-notify.org/Open-Notify-API/
url = 'http://api.open-notify.org/astros.json'
respuesta = urllib.request.urlopen(url)
astronautas = json.loads(respuesta.read())

print(f"Número actual de astronautas: {astronautas['number']}")
for persona in astronautas['people']:
    print(f"{persona['name']} está en {persona['craft']}")
    
url = 'http://api.open-notify.org/iss-now.json'
respuesta = urllib.request.urlopen(url)
iss_posicion = json.loads(respuesta.read())
coordenadas = iss_posicion['iss_position']
lat = float(coordenadas['latitude'])
long = float(coordenadas['longitude'])
print(f"Latitud: {lat}, Longitud: {long}")

Posicionando la ISS en un mapa.


Sería útil mostrar su posición en un mapa. Podemos hacer esto fácilmente usando el módulo turtle. Lo primero que haremos será cargar una imagen de fondo facilitada por la NASA.

mapa del mundo


El mapa ya esta centrado en las coordenadas (0, 0) que es justo lo que necesitamos.

También tenemos que hacer coincidir el tamaño de la ventana con el tamaño de nuestra imagen que es de 720 x 360 pixels.

En definitivas cuentas el código que utilizaremos es el siguiente:

# image source:
# map.jpg: https://visibleearth.nasa.gov/view.php?id=57752 Credit: NASA
screen = turtle.Screen()
screen.setup(720, 360)
Por otra parte también necesitamos que sea posible enviar a la tortuga (el puntero del módulo turtle) a una determinada posición en el mapa. Para hacerlo lo más sencillo posible, haremos que coincidan las coordenadas con el tamaño del mapa de la siguiente forma, utilizando el método setworldcoordinates:

# map.jpg: https://visibleearth.nasa.gov/view.php?id=57752 Credit: NASA
screen = turtle.Screen()
screen.setup(720, 360)
screen.setworldcoordinates(-180, -90, 180, 90)
screen.bgpic('map.gif')
 Con esto la coordenada inferior (-90, -180) coincidirá con la esquina inferior izquierda del mapa y (90, 180) con la esquina superior derecha del mismo.

Para que el puntero no sea una tortuga, parece más lógico que usemos otro icono que se parezca más a la ISS. Para ello la imagen tiene que ser pequeña, para que no ocupe una gran parte de la pantalla.  Un tamaño de 50 * 50 pixeles ya es bastante grande. 

Si por ejemplo tenemos el icono en un archivo llamado "station.gif" lo primero que tenemos que hacer es registrar la imagen con screen

screen = turtle.Screen()
screen.register_shape('station.gif')

Luego lo establecemos con shape.

turtle.shape('station.gif')

Normalmente el puntero de la tortuga mira hacia la derecha, pero podemos hacer que empiece mirando hacia arriba con:

turtle.setheading(90) 

La tortuga empieza siempre en el centro de la pantalla, con lo cual tenemos que moverlo a las coordenadas correctas, las que nos proporciona la longitud y latitud dada. Tendremos que levantar el lápiz o el puntero de la tortuga para que no se marque el trazo y luego llevarla hasta el punto final:

iss.penup()
iss.goto(long, lat)


Con todo esto nuestro código quedaría de la siguiente forma:

""" ¿Donde está la Estación Espacial Internacional? """

import urllib.request
import json
import turtle

# http://open-notify.org/Open-Notify-API/
url = 'http://api.open-notify.org/astros.json'
respuesta = urllib.request.urlopen(url)
astronautas = json.loads(respuesta.read())

print(f"Número actual de astronautas: {astronautas['number']}")
for persona in astronautas['people']:
    print(f"{persona['name']} está en {persona['craft']}")
    
url = 'http://api.open-notify.org/iss-now.json'
respuesta = urllib.request.urlopen(url)
iss_posicion = json.loads(respuesta.read())
coordenadas = iss_posicion['iss_position']
lat = float(coordenadas['latitude'])
long = float(coordenadas['longitude'])
print(f"Latitud: {lat}, Longitud: {long}")
    
# map.jpg: https://visibleearth.nasa.gov/view.php?id=57752 Credit: NASA
screen = turtle.Screen()
screen.setup(720, 360)
screen.setworldcoordinates(-180, -90, 180, 90)
screen.bgpic('map.gif')

screen.register_shape('station.gif')
iss = turtle.Turtle()
iss.shape('station.gif')
# iss.setheading(90) La tortuga mira a la derecha por defecto.

iss.penup()
iss.goto(long, lat)
NOTA: La latitud normalmente se pone en primer lugar, pero nosotros necesitamos hacerlo al revés ya que las coordenadas se expresan en los ejes (x, y)

Prueba el programa. La ISS debería aparecer en su actual situación sobre la tierra.


posición actual de la ISS


Espera unos pocos segundos y vuelve a ejecutarlo para ver como la ISS se desplaza.

Añadiendo datos al mapa.


Ahora que hemos obtenido los datos que necesitábamos y hemos dibujado la posición de la ISS, podemos añadir algo de información al mapa. 

Empezaremos creando una nueva tortuga para escribir algo de texto.

num_personas = turtle.Turtle()


La nueva tortuga no debería dibujar líneas mientras está en la pantalla por lo que debemos ocultarla.

num_personas = turtle.Turtle()
num_personas.penup()
num_personas.hideturtle()

Escoge un color para el texto que quieras escribir y también la posición en el mapa donde quieras que se escriba.

# color del texto
num_personas.color('yellow')
# Ir al punto en el mapa
num_personas.goto(-175,-25)

Una vez escogido el color y la posición vamos a escribir el texto que queramos en el mapa. En este caso el texto informará al usuario del número de astronautas en el espacio.

num_personas.write(f"Nº Astronautas: {astronautas['number']}")

Puedes escribir el texto que tu quieras.

Al final del proyecto quedaría una imagen como esta:

imagen final del proyecto



Escribiendo texto con turtle.


Como has visto podemos usar turtle para escribir texto.

turtle.write('¡Hola!')

También podemos elegir el color del mismo.

turtle.color('deep pink')
turtle.write('¡Hola!')

Y también podemos cambiar la fuente y la alineación del mismo.

estilo = ('Courier', 30, 'italic')
turtle.write('Hello!', font=estilo, align='center')

La fuente es una tupla que contiene:
  • El nombre de la fuente: "Arial", "Courier" o "Times New Roman".
  • El tamaño de la fuente en pixeles.
  • El tipo de fuente: "normal", "bold" o"italic".
La alineación controla como se posiciona el texto basándose en la posición de la tortuga. La alineación puede tener tres opciones: "left", "center" o "right".

La ISS en movimiento.


Vamos a hacer una pequeña variación del código anterior para que la ISS actualice su posición en el mapa cada 5 segundos y además vaya dejando una traza en color amarillo de los lugares por donde ha pasado. Algo como esto:

trazado de la ruta de la ISS



Haremos solamente unos pequeños cambios. En vez de la librería urllib.request que viene de forma predeterminada con Python usaremos la librería requests.

Para ello comenzaremos instalando la librería.

python install requests

Capturando datos de páginas web con requests.

Algunas veces necesitaremos capturar datos desde una página web. Estos pueden ser datos proporcionados por una web API o quizás código HTML de la página. En cualquier caso, el módulo de Python requests hace que esta tarea sea muy fácil.

Veamos un poco como funciona. Como siempre, lo primero es importar el módulo.

import requests

Crearemos una variable que guarde la URL de la cual queremos capturar los datos. Para este ejemplo vamos a capturar la página de Inicio de Rapsberry pi org.

URL = "https://www.raspberrypi.org"

A continuación, le diremos al programa que capture la página web y la guarde en una variable.

r = requests.get(URL)

Esta variable "r" contiene un objeto con una gran cantidad de información. Puedes examinarla usando el shell. Por ejemplo:

>>> r
<RESPONSE [200]>
Este es un código de respuesta HTTP.  En este caso el código 200 es que todo ha ido bien. puedes obtener más información sobre los diferentes códigos de respuesta aquí.

Para obtener el contenido actual de la página, puedes usar r.text. Si quieres guardar el contenido puedes hacer algo como esto:

import requests
url = "https://www.raspberrypi.org"
r = requests.get(url)
data = r.text

Con frecuencia, los datos que obtenemos de una página web suelen estar en formato json(). De forma muy sencilla podemos convertirlos en diccionarios de Python usando el método json().

import requests
url = "https://www.raspberrypi.org"
r = requests.get(url)
data = r.json()


Ahora que hemos visto como funciona el módulo requests, vamos a modificar el código del programa para lograr que refleje el movimiento de la ISS:

"""¿Donde está la Estación Espacial Internacional?."""

import requests
import turtle
import time

# http://open-notify.org/Open-Notify-API/
URL = 'http://api.open-notify.org/astros.json'
with requests.get(URL) as respuesta:
    print(respuesta)
    print(respuesta.text) # en formato json
    astronautas = respuesta.json() # en formato diccionario
    print(astronautas)

print(f"Número actual de astronautas: {astronautas['number']}")
for persona in astronautas['people']:
    print(f"{persona['name']} está en {persona['craft']}")
    
# map.jpg: https://visibleearth.nasa.gov/view.php?id=57752 Credit: NASA
screen = turtle.Screen()
screen.setup(720, 360)
screen.setworldcoordinates(-180, -90, 180, 90)
screen.bgpic('map.gif')

screen.register_shape('station.gif')
iss = turtle.Turtle()
iss.shape('station.gif')
# iss.setheading(90) La tortuga mira a la derecha por defecto
iss.penup()
screen.colormode(255)
iss.pencolor(255,255,0)

while True:
    URL = 'http://api.open-notify.org/iss-now.json'
    with requests.get(URL) as respuesta:
        iss_posicion = respuesta.json()
    coordenadas = iss_posicion['iss_position']
    lat = float(coordenadas['latitude'])
    long = float(coordenadas['longitude'])
    print(f"Latitud: {lat}, Longitud: {long}")    
    iss.goto(long, lat)
    iss.pendown()
    time.sleep(5)


Capturando los datos de vuelo de la ISS.


He modificado un poco el programa para que guarde los datos de latitud y longitud de la ISS mientras da una vuelta completa a la tierra. Para analizarlos he guardado los datos en un archivo CSV.

¿Qué es un archivo CSV?

Un archivo CSV contiene líneas de datos separados por comas. Puede también haber una cabecerá que describa de que datos se tratan. Este tipo de datos son ampliamente utilizados para guardar tablas de datos. Para leerlos y analizarlos puedes usar Python o también cualquier hoja de datos.

El código que puedes usar para guardar los datos es el siguiente:

"""¿Donde está la Estación Espacial Internacional?."""

import requests
import turtle
import time
import csv

# http://open-notify.org/Open-Notify-API/
URL = 'http://api.open-notify.org/astros.json'
with requests.get(URL) as respuesta:
    print(respuesta)
    print(respuesta.text) # en formato json
    astronautas = respuesta.json() # en formato diccionario
    print(astronautas)

print(f"Número actual de astronautas: {astronautas['number']}")
for persona in astronautas['people']:
    print(f"{persona['name']} está en {persona['craft']}")
    
# map.jpg: https://visibleearth.nasa.gov/view.php?id=57752 Credit: NASA
screen = turtle.Screen()
screen.setup(720, 360)
screen.setworldcoordinates(-180, -90, 180, 90)
screen.bgpic('map.gif')

screen.register_shape('station.gif')
iss = turtle.Turtle()
iss.shape('station.gif')
# iss.setheading(90) La tortuga mira a la derecha por defecto
iss.penup()
screen.colormode(255)
iss.pencolor(255,255,0)

# contador para guardar la long y latitud
contador = 0

while True:
    contador += 1
    URL = 'http://api.open-notify.org/iss-now.json'
    with requests.get(URL) as respuesta:
        iss_posicion = respuesta.json()
    coordenadas = iss_posicion['iss_position']
    lat = float(coordenadas['latitude'])
    long = float(coordenadas['longitude'])
    print(f"Latitud: {lat}, Longitud: {long}")    
    iss.goto(long, lat)
    iss.pendown()
    # Escribimos los datos en un archivo csv
    with open('ejemplo.csv', 'a') as file:
        datos_a_escribir = csv.writer(file)
        datos_a_escribir.writerow([lat, long])
    time.sleep(5)
    # La ISS tarda aproximadamente 91 minutos en dar
    # una vuelta a la tiera. 91*12 = 1092
    # Si los datos se toman cada 5 segundos hay que guardar
    # 60 : 5 = 12 veces cada minuto.
    if contador == 1092:
        break
El comienzo del archivo ejemplo.csv mostrará algo como esto:

-51.1014,142.0155
-51.0416,142.5997
-50.9841,143.1338
-50.9295,143.6179
-50.8669,144.1493
-50.8017,144.679
-50.7339,145.2071
-50.6635,145.7336
-50.5973,146.2108
...
Tenía la curiosidad de comprobar con estos datos si podemos encontrar una aproximación a la velocidad que lleva la ISS que según la WIKIPEDIA es de unos 7,60 Km/s.

Buscando en internet aparece que para calcular la velocidad de un satélite basándonos en los datos de la latitud y longitud junto con el tiempo transcurrido entre cada medición (5 segundos) tenemos que utilizar la fórmula de la velocidad promedio. Esta se define como el cambio de posición dividido por el tiempo transcurrido. 

Dado que tenemos los datos de la latitud y longitud cada 5 segundos, podemos calcular la distancia entre dos puntos consecutivos utilizando la fórmula de la distancia entre dos puntos de la superficie de una esfera (ya que más o menos podemos considerar la tierra como una esfera). Esta formula se conoce como la fórmula de Haversine. 

Luego, podemos calcular la velocidad promedio entre dos puntos consecutivos dividiendo la distancia entre ellos por el tiempo transcurrido. Sumando todas las velocidades promedio y dividiendo por el número total de intervalos de tiempo, obtendremos una velocidad promedio general. 

Aquí tienes el código de como podríamos implementar esto en Python:

import csv
from math import radians, sin, cos, sqrt, atan2

# Función para calcular la distancia entre dos puntos en la superficie de la Tierra
def calcular_distancia(lat1, lon1, lat2, lon2):
    # Radio de la Tierra en kilómetros
    radio_tierra = 6371.0

    # Convertir coordenadas de grados a radianes
    lat1 = radians(lat1)
    lon1 = radians(lon1)
    lat2 = radians(lat2)
    lon2 = radians(lon2)

    # Diferencia de latitud y longitud
    dlat = lat2 - lat1
    dlon = lon2 - lon1

    # Fórmula de Haversine
    a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
    c = 2 * atan2(sqrt(a), sqrt(1 - a))

    # Distancia entre los dos puntos
    distancia = radio_tierra * c

    return distancia

# Coordenadas y tiempo
with open('ejemplo.csv', 'r') as file:
    datos = csv.reader(file)
    coordenadas = []
    for dato in datos:
        dato_x, dato_y = dato
        # convertimos las cadenas en numeros
        dato_x = float(dato_x)
        dato_y = float(dato_y)
        coordenadas.append((dato_x,dato_y))
        
# coordenadas = [
#     (-40.2785, 56.2131),
#     (-40.4843, 56.5698),
#     # Insertamos nuestras coordenadas reales de esta forma.
# ]

tiempo_por_punto = 5  # segundos

# Calcular la velocidad promedio
distancias = []
for i in range(1, len(coordenadas)):
    lat1, lon1 = coordenadas[i - 1]
    lat2, lon2 = coordenadas[i]
    distancia = calcular_distancia(lat1, lon1, lat2, lon2)
    distancias.append(distancia)

# Calcular la velocidad promedio
velocidades = [distancia / tiempo_por_punto for distancia in distancias]
velocidad_promedio = sum(velocidades) / len(velocidades)

print("La velocidad promedio del satélite es:", velocidad_promedio, "km/s")

SALIDA:

La velocidad promedio del satélite es: 7.467342537150308 km/s

¡Bastante parecido a la velocidad real!

Puede encontrar el código de este proyecto en el siguiente enlace de Github.

No hay comentarios:

Publicar un comentario