Created
November 17, 2024 14:55
-
-
Save albcunha/13f112eeb9f50d70cfe24040d6a44e56 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pvlib | |
import pandas as pd | |
import numpy as np | |
from scipy.optimize import minimize | |
import pytz | |
from pytz import timezone | |
from sunposition import observed_sunpos | |
from skyfield.api import Topos, load | |
import numpy as np | |
import folium | |
def find_latitudes_for_solar_angle(utc_time, solar_angle, longitude=0, tolerance=0.1): | |
""" | |
Find possible latitudes for a given solar angle at a specific UTC time. | |
Parameters: | |
utc_time: tuple - UTC time (year, month, day, hour, minute, second) | |
solar_angle: float - Desired solar elevation angle in degrees | |
longitude: float - Longitude to check (default is 0 for Greenwich) | |
tolerance: float - Tolerance for matching the solar angle | |
Returns: | |
list of latitudes where the solar angle matches | |
""" | |
# Load timescale and ephemeris | |
ts = load.timescale() | |
planets = load("de421.bsp") | |
earth = planets["earth"] | |
sun = planets["sun"] | |
# Define UTC time | |
time = ts.utc(*utc_time) | |
# Iterate through latitudes | |
min_south_lat, max_south_lat = -90, 0 | |
min_north_lat, max_north_lat = 90, 0 | |
for lat in np.linspace(-90, 90, 1000): # Adjust step size for precision | |
# Define the observer's location | |
observer = earth + Topos(latitude_degrees=lat, longitude_degrees=longitude) | |
# Observe the Sun from the observer's location | |
astrometric = observer.at(time).observe(sun).apparent() | |
# Get the altitude (elevation) of the Sun | |
altitude, _, _ = astrometric.altaz(temperature_C=24.0, pressure_mbar=1005) | |
# Check if the altitude matches the desired solar angle within the tolerance | |
if abs(altitude.degrees - solar_angle) <= tolerance: | |
lat = float(lat) | |
if lat < 0: | |
if lat > min_south_lat: | |
min_south_lat = lat | |
if lat < max_south_lat: | |
max_south_lat = lat | |
if lat > 0: | |
if lat < min_north_lat: | |
min_north_lat = lat | |
if lat > max_north_lat: | |
max_north_lat = lat | |
return { | |
"min_south_lat": min_south_lat, | |
"max_south_lat": max_south_lat, | |
"min_north_lat": min_north_lat, | |
"max_north_lat": max_north_lat, | |
} | |
# REAL REFERENCE DATA | |
# https://www.suncalc.org/#/-7.1457,-34.8044,19/2016.09.02/09:38/1/1 | |
# Example usage | |
utc_time = (2016, 9, 2, 12, 38, 0) # YYYY, MM, DD, HH, MM, SS in UTC | |
solar_angle = 60.87 # Solar elevation angle in degrees | |
lat_long_dict = {} | |
precision_degrees = 1 | |
for long in range(-180, 180, precision_degrees): | |
possible_latitudes = find_latitudes_for_solar_angle(utc_time, solar_angle, long) | |
if ( | |
possible_latitudes["min_south_lat"] != -90 | |
or possible_latitudes["max_south_lat"] != 0 | |
or possible_latitudes["min_north_lat"] != 90 | |
or possible_latitudes["max_north_lat"] != 0 | |
): | |
print( | |
f"Possible latitudes for long {long} at solar angle {solar_angle}° at {utc_time}: {possible_latitudes}" | |
) | |
lat_long_dict[long] = possible_latitudes | |
# Function to create polygons for the latitude range | |
def create_range_polygons(lat_long_dict): | |
southern_polygon = [] | |
northern_polygon = [] | |
for long, data in sorted(lat_long_dict.items()): | |
# Add points for southern range (min and max latitudes at this longitude) | |
if data["min_south_lat"] != -90 and data["max_south_lat"] != 0: | |
southern_polygon.append([data["min_south_lat"], long]) | |
southern_polygon.insert( | |
0, [data["max_south_lat"], long] | |
) # Insert to close ring | |
# Add points for northern range (min and max latitudes at this longitude) | |
if data["min_north_lat"] != 90 and data["max_north_lat"] != 0: | |
northern_polygon.append([data["min_north_lat"], long]) | |
northern_polygon.insert( | |
0, [data["max_north_lat"], long] | |
) # Insert to close ring | |
return southern_polygon, northern_polygon | |
# Function to add polygons to the map | |
def add_range_to_map(map_object, polygon, color, popup_text): | |
if len(polygon) > 2: # Ensure there are enough points to create a polygon | |
folium.Polygon( | |
locations=polygon, | |
color=color, | |
fill=True, | |
fill_opacity=0.5, | |
popup=folium.Popup(popup_text), | |
).add_to(map_object) | |
# Main function to plot latitude ranges as rings | |
def plot_latitude_range_rings(lat_long_dict): | |
# Create a base map | |
map_center = [0, 0] | |
folium_map = folium.Map(location=map_center, zoom_start=2) | |
# Generate polygons for southern and northern ranges | |
southern_polygon, northern_polygon = create_range_polygons(lat_long_dict) | |
# Add the southern and northern polygons to the map | |
add_range_to_map( | |
folium_map, | |
southern_polygon, | |
color="red", | |
popup_text="Southern Hemisphere Range", | |
) | |
add_range_to_map( | |
folium_map, | |
northern_polygon, | |
color="blue", | |
popup_text="Northern Hemisphere Range", | |
) | |
# Save the map to an HTML file | |
folium_map.save("latitude_range_ring_map.html") | |
print( | |
"Map saved as 'latitude_range_ring_map.html'. Open this file in a browser to view it." | |
) | |
# Plot the latitude ranges as rings | |
plot_latitude_range_rings(lat_long_dict) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment