Skip to content

Instantly share code, notes, and snippets.

@albcunha
Created November 17, 2024 14:55
Show Gist options
  • Save albcunha/13f112eeb9f50d70cfe24040d6a44e56 to your computer and use it in GitHub Desktop.
Save albcunha/13f112eeb9f50d70cfe24040d6a44e56 to your computer and use it in GitHub Desktop.
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