I have two files:

1. collect_solar_data.py:
import asyncio
import aiohttp
import geopandas as gpd
import pandas as pd
from shapely.geometry import Point
import time
import logging
import diskcache as dc
from rasterio.transform import from_bounds
import numpy as np

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    handlers=[
        logging.FileHandler("solar_data_collection.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class RateLimiter:
    def __init__(self, max_calls, period):
        self.max_calls = max_calls
        self.period = period
        self.calls = []
        self.lock = asyncio.Lock()

    async def acquire(self):
        async with self.lock:
            current = time.time()
            self.calls = [t for t in self.calls if t > current - self.period]
            if len(self.calls) >= self.max_calls:
                sleep_time = self.period - (current - min(self.calls)) + 0.1
                await asyncio.sleep(sleep_time)
            self.calls.append(current)

rate_limiter = RateLimiter(max_calls=2, period=1)

async def fetch_solar_data(session, latitude, longitude, cache):
    cache_key = f"solar_data_{latitude}_{longitude}"
    if cache_key in cache:
        return cache[cache_key]

    api_url = "https://power.larc.nasa.gov/api/temporal/daily/point"
    params = {
        "parameters": "ALLSKY_SFC_SW_DWN",
        "community": "SB",
        "longitude": longitude,
        "latitude": latitude,
        "start": "20200101",
        "end": "20201231",
        "format": "JSON"
    }

    try:
        await rate_limiter.acquire()
        async with session.get(api_url, params=params, timeout=30) as response:
            if response.status == 429:
                logger.warning("Rate limit exceeded, sleeping for 60 seconds")
                await asyncio.sleep(60)
                return None
            elif response.status != 200:
                logger.error(f"Error {response.status} for ({latitude}, {longitude})")
                return None

            data = await response.json()
            solar_data = data.get("properties", {}).get("parameter", {}).get("ALLSKY_SFC_SW_DWN", {})
            if not solar_data:
                return None

            avg_radiation = sum(solar_data.values()) / len(solar_data) * 0.0036
            result = {
                "latitude": latitude,
                "longitude": longitude,
                "potential": round(avg_radiation, 2)
            }
            cache[cache_key] = result
            return result

    except Exception as e:
        logger.error(f"Error fetching data for ({latitude}, {longitude}): {e}")
        return None

async def process_grid_points(grid_points, batch_size=50):
    cache = dc.Cache('nasa_power_cache')
    solar_data = []
    
    async with aiohttp.ClientSession() as session:
        for i in range(0, len(grid_points), batch_size):
            batch = grid_points.iloc[i:i+batch_size]
            tasks = [fetch_solar_data(session, point.y, point.x, cache) 
                    for point in batch.geometry]
            
            results = await asyncio.gather(*tasks)
            valid_results = [r for r in results if r is not None]
            solar_data.extend(valid_results)
            
            logger.info(f"Processed batch {i//batch_size + 1}/{(len(grid_points) + batch_size - 1)//batch_size}")
            
            if i % 1000 == 0:
                df = pd.DataFrame(solar_data)
                df.to_csv('solar_data_partial.csv', index=False)
    
    cache.close()
    return solar_data

def create_grid_points(geojson_path='india-soi.geojson', resolution=5000):
    india = gpd.read_file(geojson_path)
    india_proj = india.to_crs('EPSG:32644')
    bounds = india_proj.total_bounds

    width = int((bounds[2] - bounds[0]) / resolution)
    height = int((bounds[3] - bounds[1]) / resolution)

    x_coords = np.linspace(bounds[0], bounds[2], width)
    y_coords = np.linspace(bounds[1], bounds[3], height)
    xx, yy = np.meshgrid(x_coords, y_coords)

    points = [Point(x, y) for x, y in zip(xx.flatten(), yy.flatten())]
    grid_gdf = gpd.GeoDataFrame(geometry=points, crs=india_proj.crs)
    
    india_union = india_proj.unary_union
    valid_points = grid_gdf[grid_gdf.within(india_union)]
    
    return valid_points.to_crs('EPSG:4326')

async def main():
    try:
        grid_points = create_grid_points()
        logger.info(f"Created {len(grid_points)} grid points")
        
        solar_data = await process_grid_points(grid_points)
        df = pd.DataFrame(solar_data)
        df.to_csv('india_solar_data.csv', index=False)
        logger.info("Data collection completed")
        
    except Exception as e:
        logger.error(f"Error in main: {e}")

if __name__ == "__main__":
    asyncio.run(main())

2. visualize_solar_data.py:

import geopandas as gpd
import pandas as pd
import numpy as np
import folium
import branca.colormap as cm
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from rasterio.transform import from_bounds
from rasterio.features import geometry_mask
from shapely.geometry import Point
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def create_solar_map(solar_data_path='india_solar_data.csv', geojson_path='india-soi.geojson'):
    solar_df = pd.read_csv(solar_data_path)
    india = gpd.read_file(geojson_path)
    india_proj = india.to_crs('EPSG:32644')

    solar_gdf = gpd.GeoDataFrame(
        solar_df,
        geometry=[Point(xy) for xy in zip(solar_df.longitude, solar_df.latitude)],
        crs='EPSG:4326'
    )
    solar_gdf_proj = solar_gdf.to_crs(india_proj.crs)

    bounds = india_proj.total_bounds
    resolution = 5000
    width = int((bounds[2] - bounds[0]) / resolution)
    height = int((bounds[3] - bounds[1]) / resolution)
    transform = from_bounds(bounds[0], bounds[1], bounds[2], bounds[3], width, height)

    raster_data = np.zeros((height, width), dtype=np.float32)
    raster_data[:] = np.nan

    for idx, row in solar_gdf_proj.iterrows():
        col_float, row_float = ~transform * (row.geometry.x, row.geometry.y)
        col, row_idx = int(col_float), int(row_float)
        if 0 <= row_idx < height and 0 <= col < width:
            raster_data[row_idx, col] = row['potential']

    geometry = [feature['geometry'] for feature in india_proj.__geo_interface__['features']]
    mask_raster = geometry_mask(geometry, out_shape=(height, width), transform=transform, invert=True)
    raster_data = np.ma.masked_array(raster_data, ~mask_raster)

    fig, ax = plt.subplots(figsize=(20, 20))
    ax.set_axis_off()

    colormap = cm.LinearColormap(
        ['#440154', '#482878', '#3E4989', '#31688E', '#26828E',
         '#1F9E89', '#35B779', '#6DCD59', '#B4DE2C', '#FDE725'],
        vmin=np.nanmin(raster_data),
        vmax=np.nanmax(raster_data),
        caption='Solar Potential (kWh/m²/year)'
    )

    img = ax.imshow(
        raster_data,
        extent=india_proj.total_bounds,
        cmap=ListedColormap(colormap.colors),
        origin='lower'
    )

    india_proj.boundary.plot(ax=ax, color='black', linewidth=1)
    plt.savefig('solar_potential_high_res.png', bbox_inches='tight', pad_inches=0, dpi=300)
    plt.close(fig)

    center_lat = (india.total_bounds[1] + india.total_bounds[3]) / 2
    center_lon = (india.total_bounds[0] + india.total_bounds[2]) / 2
    m = folium.Map(location=[center_lat, center_lon], zoom_start=5, tiles='CartoDB positron')

    img_bounds = [[india.total_bounds[1], india.total_bounds[0]], 
                  [india.total_bounds[3], india.total_bounds[2]]]

    folium.raster_layers.ImageOverlay(
        name='Solar Potential',
        image='solar_potential_high_res.png',
        bounds=img_bounds,
        opacity=0.7,
        interactive=True,
        cross_origin=False,
        zindex=1,
    ).add_to(m)

    folium.GeoJson(
        india,
        style_function=lambda x: {
            'fillColor': 'none',
            'color': 'black',
            'weight': 1.5,
            'fillOpacity': 0
        },
        name='India Boundary'
    ).add_to(m)

    colormap.add_to(m)
    folium.LayerControl().add_to(m)
    m.save('india_solar_potential.html')

if __name__ == "__main__":
    create_solar_map()

I want you to create a github yaml file which runs the two programs in sequence. The file generated by the first program should be committed to the repo, which is then used by the second program to visualise. 

I also have a requirements.txt which takes care of all libraries.