GBM-data-tools/plot/earthplot.py

170 lines
6.7 KiB
Python

# earthplot.py: Plot class for Fermi Earth orbital position
#
# Authors: William Cleveland (USRA),
# Adam Goldstein (USRA) and
# Daniel Kocevski (NASA)
#
# Portions of the code are Copyright 2020 William Cleveland and
# Adam Goldstein, Universities Space Research Association
# All rights reserved.
#
# Written for the Fermi Gamma-ray Burst Monitor (Fermi-GBM)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
import numpy as np
try:
from mpl_toolkits.basemap import Basemap
except:
raise ImportError('Basemap not installed. Cannot use EarthPlot.')
from .gbmplot import GbmPlot, SAA, McIlwainL, EarthLine, FermiIcon
from .globals import *
class EarthPlot(GbmPlot):
"""Class for plotting the Earth, primarily related to Fermi's position in
orbit.
Note:
This class requires installation of Matplotlib Basemap.
Parameters:
lat_range ((float, float), optional):
The latitude range of the plot. Default value is the extent of the
Fermi orbit: (-27, 27)
lon_range ((float, float), optional):
The longitude range of the plot. Default value is (-180, 180).
saa (bool, optional): If True, display the SAA polygon. Default is True.
mcilwain (bool, optional): If True, display the McIlwain L heatmap.
Default is True.
**kwargs: Options to pass to :class:`~.gbmplot.GbmPlot`
Attributes:
ax (:class:`matplotlib.axes`): The matplotlib axes object for the plot
canvas (Canvas Backend object): The plotting canvas, if set upon
initialization.
fermi (:class:`~.gbmplot.FermiIcon`): Fermi spacecraft plot element
fig (:class:`matplotlib.figure`): The matplotlib figure object
map (:class:`mpl_toolkits.basemap.Basemap`): The Basemap object
mcilwainl (:class:`~.gbmplot.McIlwainL`): The McIlwain L heatmap
orbit (:class:`~.gbmplot.EarthLine`): The orbital path of Fermi
saa (:class:`~.gbmplot.SAA`): The SAA polygon
"""
def __init__(self, lat_range=None, lon_range=None, saa=True, mcilwain=True,
canvas=None, **kwargs):
# Default is to cover Fermi orbit which is latitude ~27S to ~27N
if lat_range is None:
lat_range = (-29.999, 30.001)
if lon_range is None:
lon_range = (-180.0, 180.0)
# scale figure size by lat and lon ranges
xsize = (lon_range[1] - lon_range[0]) / 30.0
ysize = (lat_range[1] - lat_range[0]) / 30.0
figsize = (xsize, ysize)
super().__init__(figsize=figsize, canvas=canvas, **kwargs)
self._trig_mcilwain = None
self._saa = None
self._mcilwain = None
self._orbit = None
self._fermi = None
# initialize the map, mercator projection, coarse resolution
self._m = Basemap(projection='cyl', llcrnrlat=lat_range[0],
urcrnrlat=lat_range[1], llcrnrlon=lon_range[0],
urcrnrlon=lon_range[1], lat_ts=0, resolution='c',
ax=self._ax)
# self._m.drawcoastlines()
self._m.drawparallels(np.arange(-90., 91., 30.), labels=[1, 0, 0, 0],
fontsize=12)
self._m.drawmeridians(np.arange(-180., 181., 30.), labels=[0, 0, 0, 1],
fontsize=12)
if saa:
self._saa = SAA(self._m, self._ax, color='darkred', alpha=0.4)
if mcilwain:
self._mcilwain = McIlwainL(self._m, self._ax, alpha=0.5,
saa_mask=saa)
@property
def map(self):
return self._m
@property
def saa(self):
return self._saa
@property
def mcilwainl(self):
return self._mcilwain
@property
def orbit(self):
return self._orbit
@property
def fermi(self):
return self._fermi
def add_poshist(self, data, time_range=None, trigtime=None, numpts=1000):
"""Add a Position History or Trigdat object to plot the orbital path
of Fermi and optional the position of Fermi at a particular time.
Args:
data (:class:`~gbm.data.PosHist` or :class:`~gbm.data.Trigdat`):
A Position History or Trigdat object
time_range: ((float, float), optional)
The time range over which to plot the orbit. If omitted, plots
the orbit over the entire time range of the file.
trigtime (float, optional):
If data is PosHist, set trigtime to a particular time of interest
to plot Fermi's orbital location. The Trigdat trigger time
overrides this.
numpts (int, optional): The number of interpolation points for
plotting the orbit. Default is 1000.
"""
if time_range is None:
time_range = data.time_range
# get latitude and longitude over the time range and produce the orbit
times = np.linspace(*time_range, numpts)
lat = data.get_latitude(times)
lon = data.get_longitude(times)
self._orbit = EarthLine(lat, lon, self._m, self._ax, color='black',
alpha=0.4, lw=5.0)
# if trigtime is set or Trigdat, produce Fermi location
if hasattr(data, 'trigtime'):
trigtime = data.trigtime
if trigtime is not None:
lat = data.get_latitude(trigtime)
lon = data.get_longitude(trigtime)
self._trig_mcilwain = data.get_mcilwain_l(trigtime)
self._fermi = FermiIcon(lat, lon, self._m, self._ax)
def standard_title(self):
"""Add the standard plot title to the plot if a Trigdat object or a
PosHist object + trigtime has been added.
"""
if self.fermi is not None:
coord = self.fermi.coordinates
title = 'Latitude, East Longitude: ({0}, {1})\n'.format(*coord)
title += 'McIlwain L: {}'.format(
'{:.2f}'.format(float(self._trig_mcilwain)))
self._ax.set_title(title)