{ "cells": [ { "cell_type": "markdown", "id": "cb2fcaa3", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "# Introduction\n", "\n", "Ground measured solar irradiance data is critical for benchmarking solar radiation products, modeling climate processes, and understanding the Earth's radiation budget. However, due to high costs and maintenance requirements, there are only a few hundred high-quality stations globally. Partly due to the scarcity and even more so due to a lack of an overview, it has historically been difficult to determine if and where there is a nearby solar irradiance monitoring station. To address this, this site provides an overview of multi-component solar irradiance monitoring stations worldwide and supporting [metadata](station_metadata).\n", "\n", "A complete list of stations and metadata can be found in the [station catalog](../station_catalog).\n", "\n", "For more details on the SolarStations.org catalog, the reader is referred to the data article published in Solar Energy, DOI: [10.1016/j.solener.2025.113457](https://doi.org/10.1016/j.solener.2025.113457). We hope that you will cite the article if you use the catalog in published works.\n", "\n", "To find the nearest station to a point of interest, check out the interactive map below. Note that it is possible to click on a station icon to get the station name and country." ] }, { "cell_type": "code", "execution_count": null, "id": "6a1101b1-fdf8-4389-a1ce-a7f997cd3b4a", "metadata": { "tags": [ "hide-input", "remove-input" ] }, "outputs": [], "source": [ "import pandas as pd\n", "import folium\n", "from folium import plugins\n", "import folium_legend\n", "\n", "# Load stations\n", "solarstations = pd.read_csv('../solarstations.csv', dtype={'Tier': str}).fillna('')\n", "esmap_stations = pd.read_csv('../esmap_stations.csv', dtype={'Tier': str}).fillna('')\n", "# Add esmap stations first so they are underneath\n", "stations = pd.concat([solarstations, esmap_stations], axis='rows', ignore_index=True)\n", "stations = stations[~stations['Instrumentation'].str.contains('G;Ds')] # remove Tier 3 stations\n", "\n", "GHIImagery = \"https://d2asdkx1wwwi7q.cloudfront.net/v20210621/ghi_global/{z}/z{z}_{x}x{y}.jpg\"\n", "DNIImagery = \"https://d2asdkx1wwwi7q.cloudfront.net/v20210621/dni_global/{z}/z{z}_{x}x{y}.jpg\"\n", "GlobalSolarAtlasAttribution = \"Source: GlobalSolarAtlas 2.0. Data from Solargis.\"\n", "EsriImagery = \"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}\"\n", "EsriAttribution = \"Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community\"\n", "\n", "# Create Folium map\n", "m = folium.Map(\n", " location=[0, 15],\n", " zoom_start=1, min_zoom=1, max_bounds=True,\n", " control_scale=True, # Adds distance scale in lower left corner\n", " tiles='Cartodb Positron')\n", "\n", "def station_status(time_period):\n", " if time_period.endswith('-'):\n", " return 'Active'\n", " color = '#008000' # Green for active stations\n", " elif (time_period == '') | time_period.endswith('?'):\n", " return 'Unknown'\n", " else:\n", " return 'Inactive'\n", "\n", "stations['Status'] = stations['Time period'].map(station_status)\n", "\n", "status_dict = {\n", " 'Active': '#008000', # Green for active stations\n", " 'Unknown': '#3186cc', # Blue for stations with unknown status\n", " 'Inactive': '#ff422b', # Red for inactive stations\n", "}\n", "\n", "stations['Color'] = stations['Status'].map(status_dict)\n", "\n", "z_order_dict = {'Active': 2, 'Unknown': 1, 'Inactive': 0}\n", "stations['z_order'] = stations['Status'].map(z_order_dict)\n", "\n", "stations['Tier'] = 2\n", "stations.loc[stations['Instrumentation'].str.startswith('G;B;D'), 'Tier'] = 1\n", "stations = stations.sort_values('z_order')\n", "\n", "annual_irradiance = pd.read_csv('../data/nasa_power_annual_irradiance_global.csv', index_col=[0, 1])\n", "for index, row in stations.iterrows():\n", " lat_round = round(row['Latitude']*2-0.5, 0)/2 + 0.25\n", " lon_round = round(row['Longitude']*2-0.5, 0)/2 + 0.25\n", " try:\n", " stations.loc[index, ['GHI', 'DHI', 'DNI']] = \\\n", " annual_irradiance.loc[(lat_round, lon_round), :].values\n", " except KeyError as e:\n", " continue\n", "\n", "# Add each station to the map\n", "for index, row in stations.iterrows(): \n", " folium.CircleMarker(\n", " location=[row['Latitude'], row['Longitude']],\n", " popup=folium.Popup(\n", " f\"{row['Station name']}, {row['Country']}
\"\n", " f\"Tier: {row['Tier']}
\"\n", " f\"Elevation: {row['Elevation']} m
\"\n", " f\"GHI: {row['GHI']:.0f} kWh/m^2
\"\n", " f\"DNI: {row['DNI']:.0f} kWh/m^2\",\n", " max_width=\"100\"),\n", " tooltip=row['Abbreviation'],\n", " radius=5, color=row['Color'],\n", " fill_color=row['Color'], fill=True).add_to(m)\n", "\n", "folium.raster_layers.TileLayer(EsriImagery, name='World imagery', attr=EsriAttribution, show=False).add_to(m)\n", "folium.raster_layers.TileLayer(GHIImagery, name='GHI', attr=GlobalSolarAtlasAttribution,\n", " max_zoom=10, max_native_zoom=10, zoomOffset=1, show=False).add_to(m)\n", "folium.raster_layers.TileLayer(DNIImagery, name='DNI', attr=GlobalSolarAtlasAttribution,\n", " max_zoom=10, max_native_zoom=10, zoomOffset=1, show=False).add_to(m)\n", "folium.LayerControl(position='topright').add_to(m)\n", "\n", "# Additional options and plugins\n", "# Note it's not possible to change the position of the scale\n", "plugins.Fullscreen(position='bottomright').add_to(m) # Add full screen button to map\n", "folium.LatLngPopup().add_to(m) # Show latitude/longitude when clicking on the map\n", "# plugins.MiniMap(toggle_display=True, zoom_level_fixed=1, minimized=True, position='bottomright').add_to(m) # Add minimap to the map\n", "# plugins.MeasureControl(position='topleft').add_to(m) # Add distance length measurement tool\n", "\n", "# Create legend\n", "legend = folium_legend.make_legend(status_dict.keys(), status_dict.values(), title=\"Station status\")\n", "m.get_root().html.add_child(legend) # Add Legend to map\n", "\n", "# Show the map\n", "m" ] }, { "cell_type": "markdown", "id": "a6627710", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "```{admonition} Map backgrounds\n", "When zooming in on a specific station, consider switching to the satellite image or annual irradiance backgrounds (top right corner).\n", "```" ] }, { "cell_type": "markdown", "id": "3ab7e85b", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "## Acknowledgements\n", "Solar irradiance map backgrounds were obtained from the Global Solar Atlas 2.0, a free, web-based application developed and operated by Solargis s.r.o. on behalf of the World Bank Group, utilizing Solargis data, with funding provided by the Energy Sector Management Assistance Program (ESMAP). For additional information see https://globalsolaratlas.info." ] }, { "cell_type": "code", "execution_count": null, "id": "8bd32a47-e9cf-42f9-be49-8c2b244e4283", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.7" }, "toc-showcode": false, "toc-showtags": true }, "nbformat": 4, "nbformat_minor": 5 }