Auto-update: Fri Nov 15 13:05:58 PST 2024

This commit is contained in:
sanj 2024-11-15 13:05:58 -08:00
parent 2d49d296fb
commit f44d2e7122

View file

@ -128,7 +128,7 @@ async def generate_and_save_heatmap(
output_path: Optional[Path] = None output_path: Optional[Path] = None
) -> Path: ) -> Path:
""" """
Generate a heatmap for the given date range and save it as a PNG file using matplotlib. Generate a heatmap for the given date range and save it as a PNG file.
:param start_date: The start date for the map (or the only date if end_date is not provided) :param start_date: The start date for the map (or the only date if end_date is not provided)
:param end_date: The end date for the map (optional) :param end_date: The end date for the map (optional)
@ -137,7 +137,9 @@ async def generate_and_save_heatmap(
""" """
try: try:
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import contextily as ctx
import numpy as np import numpy as np
from matplotlib.colors import LinearSegmentedColormap
start_date = await dt(start_date) start_date = await dt(start_date)
if end_date: if end_date:
@ -149,27 +151,56 @@ async def generate_and_save_heatmap(
if not locations: if not locations:
raise ValueError("No locations found for the given date range") raise ValueError("No locations found for the given date range")
lats = [loc.latitude for loc in locations] lats = np.array([loc.latitude for loc in locations])
lons = [loc.longitude for loc in locations] lons = np.array([loc.longitude for loc in locations])
plt.style.use('dark_background') # Calculate bounds with 5% buffer
fig, ax = plt.subplots(figsize=(10, 6)) lat_range = max(lats) - min(lats)
lon_range = max(lons) - min(lons)
buffer = max(lat_range, lon_range) * 0.05
# Create heatmap # Enforce minimum zoom
heatmap, xedges, yedges = np.histogram2d(lons, lats, bins=50) MIN_RANGE = 0.05 # roughly 3-4 miles
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] lat_range = max(lat_range, MIN_RANGE)
lon_range = max(lon_range, MIN_RANGE)
# Plot with no axes or labels bounds = [
ax.imshow(heatmap.T, extent=extent, origin='lower', cmap='hot', interpolation='gaussian') min(lons) - buffer,
ax.axis('off') max(lons) + buffer,
min(lats) - buffer,
max(lats) + buffer
]
# Remove white border # Create figure with fixed size
plt.gca().set_position([0, 0, 1, 1]) fig, ax = plt.subplots(figsize=(6.4, 3.6), dpi=100) # 640x360 pixels
# Add dark basemap
ctx.add_basemap(
ax,
crs='EPSG:4326',
source=ctx.providers.CartoDB.DarkMatter,
zoom='auto',
bbox=bounds
)
# Create heatmap overlay
heatmap = ax.hexbin(
lons, lats,
extent=bounds,
gridsize=25,
cmap='hot',
alpha=0.6,
zorder=2
)
# Remove axes and margins
ax.set_axis_off()
plt.subplots_adjust(left=0, right=1, top=1, bottom=0)
if output_path is None: if output_path is None:
output_path, relative_path = assemble_journal_path(end_date, filename="map", extension=".png", no_timestamp=True) output_path, relative_path = assemble_journal_path(end_date, filename="map", extension=".png", no_timestamp=True)
plt.savefig(output_path, bbox_inches='tight', pad_inches=0, transparent=True) plt.savefig(output_path, bbox_inches='tight', pad_inches=0, dpi=100)
plt.close() plt.close()
l.info(f"Heatmap saved as PNG: {output_path}") l.info(f"Heatmap saved as PNG: {output_path}")
@ -180,6 +211,7 @@ async def generate_and_save_heatmap(
raise raise
async def generate_map(start_date: datetime, end_date: datetime, max_points: int): async def generate_map(start_date: datetime, end_date: datetime, max_points: int):
locations = await fetch_locations(start_date, end_date) locations = await fetch_locations(start_date, end_date)
if not locations: if not locations: