Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- # Filename: nasa_epic_metadata.py
- # Version: 1.0.0
- # Author: Jeoi Reqi
- """
- Description:
- - This script fetches image metadata from NASA's EPIC (Earth Polychromatic Imaging Camera) API.
- - EPIC is a camera onboard the NOAA's DSCOVR (Deep Space Climate Observatory) spacecraft,
- positioned at the Lagrange point 1 (L1), approximately one million miles from Earth.
- - The camera captures images of the entire sunlit side of Earth, providing a unique perspective on our planet.
- The EPIC images are available in four different types:
- - Natural: True-color images that show Earth as it appears to the human eye.
- - Enhanced: Images that are adjusted to enhance specific features of the atmosphere and surface.
- - Aerosol: Images that highlight the distribution and concentration of aerosols in the atmosphere.
- - Cloud: Images that provide detailed views of cloud formations and their dynamics.
- Users can specify a date range to fetch metadata for, and the metadata will be saved in the specified folder.
- The metadata includes information such as:
- - Identifier: A unique identifier for each image.
- - Image: The name of the image file.
- - Date: The date and time when the image was captured.
- - Centroid Coordinates: The latitude and longitude coordinates of the image's center.
- - DSCOVR J2000 Position: The position of the DSCOVR spacecraft in J2000 coordinates (X, Y, Z).
- - Lunar J2000 Position: The position of the Moon in J2000 coordinates (X, Y, Z).
- - Sun J2000 Position: The position of the Sun in J2000 coordinates (X, Y, Z).
- Requirements:
- - Python 3.x
- - Required modules:
- - requests
- - datetime
- - time
- - dateutil
- - colorama
- Functions:
- - make_api_request:
- Makes an API request to the specified URL.
- - check_rate_limit:
- Checks and manages the API call rate limit.
- - write_metadata_to_file:
- Writes the fetched metadata to a file.
- Classes:
- - NASAEPICManager:
- A class to manage NASA EPIC API requests and rate limits.
- Usage:
- 1. Ensure there's a file named 'nasa_api.txt' in the current working directory containing your NASA API Key(s).
- The API key file contents must be separated by a new line if you need to use multiple keys.
- 2. Run the script.
- 3. Follow the on-screen prompts to select an option and specify the date range.
- 4. The metadata will be fetched and saved in a text file.
- Additional Notes:
- - You need to obtain a NASA API key to use this script.
- - You can get it by registering on the [NASA API portal](https://api.nasa.gov/index.html?apply-for-an-api-key).
- - After obtaining your API key, create a text file named 'nasa_api.txt' in the same directory as this script.
- - Save your API key(s) in 'nasa_api.txt', with each key on a new line if you have multiple keys. Example:
- - EXAMPLE:
- - YOUR_FIRST_API_KEY
- - YOUR_SECOND_API_KEY
- - To download the image files, you can go to URL: (https://pastebin.com/2KQKTxe2)
- - Save the script as 'nasa_epic_images.py' in your current working directory.
- - Follow the detailed prompting from the program
- """
- # Get Essential Imports
- import time
- import requests
- from datetime import datetime, timedelta
- from dateutil import parser as date_parser
- from colorama import init, Fore, Style
- # Initialize colorama
- init(autoreset=True)
- class NASAEPICManager:
- """
- A class to manage NASA EPIC API requests and rate limits.
- Attributes:
- api_keys (list): List of API keys obtained from a file.
- current_key_index (int): Index of the current API key being used.
- hourly_limit (int): Maximum number of API calls allowed per hour.
- calls_count (int): Number of API calls made in the current hour.
- last_reset_time (float): Timestamp of the last reset time for the hourly limit.
- """
- def __init__(self, api_keys_file):
- """
- Initializes the NASAEPICManager with API keys and rate limit parameters.
- Args:
- api_keys_file (str): Path to the file containing API keys.
- """
- with open(api_keys_file, 'r', encoding='utf-8') as file:
- self.api_keys = file.read().splitlines()
- self.current_key_index = 0
- self.hourly_limit = 999
- self.calls_count = 0
- self.last_reset_time = time.time()
- def make_api_request(self, url, retries=3):
- """
- Makes an API request to the specified URL.
- Args:
- url (str): The URL to make the API request to.
- retries (int): Number of retries in case of failure. Default is 3.
- Returns:
- dict: JSON response from the API if successful, otherwise None.
- """
- self.check_rate_limit()
- current_api_key = self.api_keys[self.current_key_index]
- params = {'api_key': current_api_key}
- for _ in range(retries):
- try:
- response = requests.get(url, params=params)
- response.raise_for_status()
- self.calls_count += 1
- print(f"\nAPI Key: {current_api_key}, Remaining Requests: {response.headers.get('X-RateLimit-Remaining', 0)}\n")
- return response.json()
- except requests.exceptions.RequestException:
- print(f"\nError: No image metadata found for {url}! Retrying...\n")
- time.sleep(3) # Wait for 3 seconds before retrying
- print(f"Failed to fetch metadata after retries for {url}.")
- return None
- def check_rate_limit(self):
- """
- Checks and manages the API call rate limit.
- Resets counters if more than one hour has passed or switches to the next API key if the hourly limit is reached.
- """
- current_time = time.time()
- time_since_reset = current_time - self.last_reset_time
- if time_since_reset >= 3600:
- # Reset counters if more than one hour has passed
- self.calls_count = 0
- self.last_reset_time = current_time
- if self.calls_count >= self.hourly_limit:
- # Switch to the next API key if the hourly limit is reached
- self.current_key_index = (self.current_key_index + 1) % len(self.api_keys)
- self.calls_count = 0
- def write_metadata_to_file(metadata_list, filename_str):
- """
- Writes metadata to a file.
- Args:
- metadata_list (list): A list of dictionaries containing metadata information.
- filename_str (str): The name of the file to write the metadata to.
- Notes:
- - Each entry in the metadata list should be a dictionary containing metadata information.
- - The function writes metadata in a formatted way to the specified file.
- - Metadata includes identifier, image name, date, centroid coordinates, and positions of DSCOVr, Lunar, and Sun.
- """
- with open(filename_str, 'w', encoding='UTF-8') as file:
- for entry in metadata_list:
- file.write(f"Identifier: #{entry['identifier']}, \tImage: {entry['image']}, \tDate: {entry['date']}\n\n")
- file.write(f"Centroid Coordinates: \tLatitude {entry['coords']['centroid_coordinates']['lat']}, \tLongitude {entry['coords']['centroid_coordinates']['lon']}\n\n")
- file.write("\t\t\t\t[X]\t\t\t[Y]\t\t\t[Z]\n\n")
- file.write(f"DSCOVr J2000 Position: \tx {entry['coords']['dscovr_j2000_position']['x']}, \ty {entry['coords']['dscovr_j2000_position']['y']}, \tz {entry['coords']['dscovr_j2000_position']['z']}\n")
- file.write(f"Lunar J2000 Position: \tx {entry['coords']['lunar_j2000_position']['x']}, \ty {entry['coords']['lunar_j2000_position']['y']}, \tz {entry['coords']['lunar_j2000_position']['z']}\n")
- file.write(f"Sun J2000 Position: \tx {entry['coords']['sun_j2000_position']['x']}, \ty {entry['coords']['sun_j2000_position']['y']}, \tz {entry['coords']['sun_j2000_position']['z']}\n\n")
- file.write("-" * 50 + "\n\n")
- # Define the header as a list of strings for easier manipulation
- header_lines = [
- " ███ ██ █████ ███████ █████ ",
- " ████ ██ ██ ██ ██ ██ ██ ",
- " ██ ██ ██ ███████ ███████ ███████ ",
- " ██ ██ ██ ██ ██ ██ ██ ██ ",
- " ██ ████ ██ ██ ███████ ██ ██ ",
- "███████ █████ ██████ ███████ █████ ██████ ██████ ███████ ",
- " ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ",
- " ██ █████ █████ ███████ ███████ █████ █████ █████ █████ ███████ ███████ ",
- " ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ",
- " ██ █████ ██████ ███████ █████ ██████ ██████ ███████ ",
- " ███████ ██████ ██ ██████ ",
- " ██ ██ ██ ██ ██ ",
- " █████ ██████ ██ ██ ",
- " ██ ██ ██ ██ ",
- " ███████ ██ ██ ██████ (METADATA) "
- ]
- # Apply colors to the specified lines
- colored_header = []
- for i, line in enumerate(header_lines):
- if 0 <= i < 5: # NASA
- colored_header.append(Fore.RED + line + Style.RESET_ALL)
- elif 5 <= i < 10: # 78-65-83-65 (ASCII CODE)
- colored_header.append(Fore.WHITE + line + Style.RESET_ALL)
- elif 10 <= i < 15: # EPIC
- colored_header.append(Fore.BLUE + line + Style.RESET_ALL)
- else:
- colored_header.append(line)
- # Print the colored header
- for line in colored_header:
- print(line)
- if __name__ == "__main__":
- nasa_manager = NASAEPICManager('nasa_api.txt')
- while True: # Loop for the menu
- # Display options for the user
- print("_" * 83)
- print("\nMetadata Options:\n")
- print("1. Natural")
- print("2. Enhanced")
- print("3. Cloud")
- print("4. Aerosol")
- print("5. Exit") # Option to exit
- # Get user's choice
- option = input("\nSelect an option (1-5): ")
- if option == '5': # Exit option
- print("\nExiting Program... GoodBye!\n")
- break # Exit the loop and end the program
- # Map the user's choice to the corresponding URL component
- option_mapping = {
- '1': 'natural',
- '2': 'enhanced',
- '3': 'cloud',
- '4': 'aerosol'
- }
- # Check if the user's choice is valid
- if option in option_mapping:
- selected_option = option_mapping[option]
- # Get current date to compare with user input
- current_date = datetime.now().date()
- # Check if start date is before the epoch date (June 13, 2015)
- epoch_dates = {
- 'natural': '2015-06-13',
- 'enhanced': '2015-08-07',
- 'aerosol': '2020-09-04',
- 'cloud': '2023-01-03'
- }
- # Get start date from the user
- print("_" * 83)
- print("\n\t\t\t:: Start Date Format: (YYYY-MM-DD) ::\n")
- start_date_str = input(f"Enter a 'Start Date' after {epoch_dates[selected_option]}: ")
- print("_" * 83)
- start_date = date_parser.parse(start_date_str).date()
- # Check if start date is before the epoch date (June 13, 2015)
- epoch_date = datetime(2015, 6, 13).date()
- if start_date < epoch_date:
- print(Fore.RED + "\nError: Start date cannot be before June 13, 2015!\n" + Style.RESET_ALL)
- continue # Restart the loop for a new input
- # Check if start date is in the future
- if start_date > current_date:
- print(Fore.RED + "\nError: Start date cannot be in the future!\n" + Style.RESET_ALL)
- continue # Restart the loop for a new input
- # Check if the user wants to log a single day or a range of dates
- end_date_str = input("\nEnter the end date (YYYY-MM-DD) or press [ENTER] to log a single day: ")
- if end_date_str:
- end_date = date_parser.parse(end_date_str).date()
- # Check if end date is in the future
- if end_date > current_date:
- print(Fore.RED + "\nError: End date cannot be in the future!\n" + Style.RESET_ALL)
- continue # Restart the loop for a new input
- # Check if end date is before the epoch date (June 13, 2015)
- if end_date < epoch_date:
- print(Fore.RED + "\nError: End date cannot be before June 13, 2015!\n" + Style.RESET_ALL)
- continue # Restart the loop for a new input
- else:
- end_date = start_date
- # Specify the filename for the formatted metadata
- filename = f'{start_date_str}_{end_date_str}_{selected_option}.txt'
- # Iterate through the date range and log data for each day
- current_date = start_date
- all_metadata = [] # List to store metadata for all dates
- while current_date <= end_date:
- date_str = current_date.strftime('%Y-%m-%d')
- metadata_url = f"https://api.nasa.gov/EPIC/api/{selected_option}/date/{date_str}?api_key=TEMPL4ujtA3LyN0qbFh4imFv7gxDfUG9WoU0eWOu"
- metadata = nasa_manager.make_api_request(metadata_url)
- if metadata:
- print(f"Metadata for {date_str}:")
- print(metadata)
- all_metadata.extend(metadata) # Append metadata to the list
- current_date += timedelta(days=1)
- # Create the file if it doesn't exist
- if all_metadata:
- write_metadata_to_file(all_metadata, filename)
- print(f"\nFormatted metadata has been written to {filename}\n")
- else:
- print("\nNo valid entries found in the specified date range.\n")
- else:
- print("\nInvalid option! Please select a valid option (1-5).\n")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement