diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5c90979..3187904 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 @@ -29,15 +29,21 @@ jobs: "${SHELL}" <(curl -L micro.mamba.pm/install.sh) eval "$(micromamba shell hook --shell bash)" micromamba activate base - micromamba install pytest-cov xarray netcdf4 -c conda-forge + micromamba install pytest-cov pytest-mpl xarray netcdf4 pandas numpy scikit-learn scipy pyproj cartopy metpy ipywidgets python=${{ matrix.python-version }} -c conda-forge python -m pip install . - name: Test with pytest run: | eval "$(micromamba shell hook --shell bash)" micromamba activate base - coverage run --source=pyxlma -m pytest tests/ + coverage run --source=pyxlma -m pytest --mpl --mpl-baseline-path=tests/truth/images/ --mpl-generate-summary=html,json --mpl-results-path=tests/mpl-results/ tests/ coverage xml - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + - name: Upload matplotlib test results + if: always() + uses: actions/upload-artifact@v3 + with: + name: matplotlib-results + path: tests/mpl-results/ diff --git a/README.md b/README.md index ae5a0d0..6213950 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,38 @@ pip install -e . Then, copy the `XLMA_plots.ipynb` notebook to wherever you'd like and start changing files, dates and times to show data from your case of interest. There also a notebook showing how to do flash sorting and save a new NetCDF file with those data. +# Dependencies +Required: + +- xarray (I/O requires the netcdf4 backend) +- pandas +- numpy + +Flash clustering: + +- scikit-learn +- scipy +- pyproj + +Plotting: + +- matplotlib +- cartopy +- metpy (optionally, for US county lines) + +Interactive: + +- jupyterlab (or, notebook) +- ipywidgets +- ipympl + +Building: + +- setuptools +- pytest-cov +- pytest-mpl +- ...and all of the above + # Technical architecture We envision a two-part package that keeps a clean separation between the core data model, analysis, and display. XLMA utilized a large, global `state` structure that stored all data, as well as the current data selection corresponding to the view in the GUI. Analysis then operated on whatever data was in the current selection. diff --git a/examples/data/lma/WTLMA_231224_005701_0001.dat.gz b/examples/data/WTLMA_231224_005701_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005701_0001.dat.gz rename to examples/data/WTLMA_231224_005701_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005702_0001.dat.gz b/examples/data/WTLMA_231224_005702_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005702_0001.dat.gz rename to examples/data/WTLMA_231224_005702_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005704_0001.dat.gz b/examples/data/WTLMA_231224_005704_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005704_0001.dat.gz rename to examples/data/WTLMA_231224_005704_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005705_0001.dat.gz b/examples/data/WTLMA_231224_005705_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005705_0001.dat.gz rename to examples/data/WTLMA_231224_005705_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005706_0001.dat.gz b/examples/data/WTLMA_231224_005706_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005706_0001.dat.gz rename to examples/data/WTLMA_231224_005706_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005707_0001.dat.gz b/examples/data/WTLMA_231224_005707_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005707_0001.dat.gz rename to examples/data/WTLMA_231224_005707_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005711_0001.dat.gz b/examples/data/WTLMA_231224_005711_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005711_0001.dat.gz rename to examples/data/WTLMA_231224_005711_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005712_0001.dat.gz b/examples/data/WTLMA_231224_005712_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005712_0001.dat.gz rename to examples/data/WTLMA_231224_005712_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005713_0001.dat.gz b/examples/data/WTLMA_231224_005713_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005713_0001.dat.gz rename to examples/data/WTLMA_231224_005713_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005714_0001.dat.gz b/examples/data/WTLMA_231224_005714_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005714_0001.dat.gz rename to examples/data/WTLMA_231224_005714_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005715_0001.dat.gz b/examples/data/WTLMA_231224_005715_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005715_0001.dat.gz rename to examples/data/WTLMA_231224_005715_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005718_0001.dat.gz b/examples/data/WTLMA_231224_005718_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005718_0001.dat.gz rename to examples/data/WTLMA_231224_005718_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005719_0001.dat.gz b/examples/data/WTLMA_231224_005719_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005719_0001.dat.gz rename to examples/data/WTLMA_231224_005719_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005720_0001.dat.gz b/examples/data/WTLMA_231224_005720_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005720_0001.dat.gz rename to examples/data/WTLMA_231224_005720_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005722_0001.dat.gz b/examples/data/WTLMA_231224_005722_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005722_0001.dat.gz rename to examples/data/WTLMA_231224_005722_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005723_0001.dat.gz b/examples/data/WTLMA_231224_005723_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005723_0001.dat.gz rename to examples/data/WTLMA_231224_005723_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005724_0001.dat.gz b/examples/data/WTLMA_231224_005724_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005724_0001.dat.gz rename to examples/data/WTLMA_231224_005724_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005725_0001.dat.gz b/examples/data/WTLMA_231224_005725_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005725_0001.dat.gz rename to examples/data/WTLMA_231224_005725_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005726_0001.dat.gz b/examples/data/WTLMA_231224_005726_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005726_0001.dat.gz rename to examples/data/WTLMA_231224_005726_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005728_0001.dat.gz b/examples/data/WTLMA_231224_005728_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005728_0001.dat.gz rename to examples/data/WTLMA_231224_005728_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005729_0001.dat.gz b/examples/data/WTLMA_231224_005729_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005729_0001.dat.gz rename to examples/data/WTLMA_231224_005729_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005730_0001.dat.gz b/examples/data/WTLMA_231224_005730_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005730_0001.dat.gz rename to examples/data/WTLMA_231224_005730_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005731_0001.dat.gz b/examples/data/WTLMA_231224_005731_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005731_0001.dat.gz rename to examples/data/WTLMA_231224_005731_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005733_0001.dat.gz b/examples/data/WTLMA_231224_005733_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005733_0001.dat.gz rename to examples/data/WTLMA_231224_005733_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005734_0001.dat.gz b/examples/data/WTLMA_231224_005734_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005734_0001.dat.gz rename to examples/data/WTLMA_231224_005734_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005736_0001.dat.gz b/examples/data/WTLMA_231224_005736_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005736_0001.dat.gz rename to examples/data/WTLMA_231224_005736_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005737_0001.dat.gz b/examples/data/WTLMA_231224_005737_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005737_0001.dat.gz rename to examples/data/WTLMA_231224_005737_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005738_0001.dat.gz b/examples/data/WTLMA_231224_005738_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005738_0001.dat.gz rename to examples/data/WTLMA_231224_005738_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005739_0001.dat.gz b/examples/data/WTLMA_231224_005739_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005739_0001.dat.gz rename to examples/data/WTLMA_231224_005739_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005740_0001.dat.gz b/examples/data/WTLMA_231224_005740_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005740_0001.dat.gz rename to examples/data/WTLMA_231224_005740_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005741_0001.dat.gz b/examples/data/WTLMA_231224_005741_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005741_0001.dat.gz rename to examples/data/WTLMA_231224_005741_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005743_0001.dat.gz b/examples/data/WTLMA_231224_005743_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005743_0001.dat.gz rename to examples/data/WTLMA_231224_005743_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005744_0001.dat.gz b/examples/data/WTLMA_231224_005744_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005744_0001.dat.gz rename to examples/data/WTLMA_231224_005744_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005745_0001.dat.gz b/examples/data/WTLMA_231224_005745_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005745_0001.dat.gz rename to examples/data/WTLMA_231224_005745_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005746_0001.dat.gz b/examples/data/WTLMA_231224_005746_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005746_0001.dat.gz rename to examples/data/WTLMA_231224_005746_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005750_0001.dat.gz b/examples/data/WTLMA_231224_005750_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005750_0001.dat.gz rename to examples/data/WTLMA_231224_005750_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005751_0001.dat.gz b/examples/data/WTLMA_231224_005751_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005751_0001.dat.gz rename to examples/data/WTLMA_231224_005751_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005753_0001.dat.gz b/examples/data/WTLMA_231224_005753_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005753_0001.dat.gz rename to examples/data/WTLMA_231224_005753_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005754_0001.dat.gz b/examples/data/WTLMA_231224_005754_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005754_0001.dat.gz rename to examples/data/WTLMA_231224_005754_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005755_0001.dat.gz b/examples/data/WTLMA_231224_005755_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005755_0001.dat.gz rename to examples/data/WTLMA_231224_005755_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005758_0001.dat.gz b/examples/data/WTLMA_231224_005758_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005758_0001.dat.gz rename to examples/data/WTLMA_231224_005758_0001.dat.gz diff --git a/examples/data/lma/WTLMA_231224_005759_0001.dat.gz b/examples/data/WTLMA_231224_005759_0001.dat.gz similarity index 100% rename from examples/data/lma/WTLMA_231224_005759_0001.dat.gz rename to examples/data/WTLMA_231224_005759_0001.dat.gz diff --git a/examples/data/lma_netcdf/lma.nc b/examples/data/lma_netcdf/lma.nc deleted file mode 100644 index 4626eb1..0000000 Binary files a/examples/data/lma_netcdf/lma.nc and /dev/null differ diff --git a/pyxlma/coords.py b/pyxlma/coords.py index 19cbb1a..d0a2589 100644 --- a/pyxlma/coords.py +++ b/pyxlma/coords.py @@ -67,6 +67,8 @@ def __init__(self, ellipse='WGS84', datum='WGS84', if (r_equator is not None) | (r_pole is not None): if r_pole is None: r_pole=r_equator + if r_equator is None: + r_equator=r_pole self.ERSlla = proj4.Proj(proj='latlong', a=r_equator, b=r_pole) self.ERSxyz = proj4.Proj(proj='geocent', a=r_equator, b=r_pole) else: diff --git a/pyxlma/lmalib/flash/properties.py b/pyxlma/lmalib/flash/properties.py index 0535ec2..4b23386 100644 --- a/pyxlma/lmalib/flash/properties.py +++ b/pyxlma/lmalib/flash/properties.py @@ -19,7 +19,7 @@ def hull_volume(xyz): assert xyz.shape[1] == 3 tri = Delaunay(xyz[:,0:3]) - vertices = tri.points[tri.vertices] + vertices = tri.points[tri.simplices] # This is the volume formula in # https://github.com/scipy/scipy/blob/master/scipy/spatial/tests/test_qhull.py#L106 @@ -287,7 +287,7 @@ def filter_flashes(ds, **kwargs): good = np.ones(ds.flash_id.shape, dtype=bool) # print("Starting flash count: ", good.sum()) if 'flash_event_count' in ds.variables: - if np.all(ds.flash_event_count == np.iinfo(np.uint64).max): + if np.all(ds.flash_event_count == np.iinfo(np.uint32).max): raise ValueError('Before filtering a dataset by flash properties, call flash_stats on the dataset to compute flash properties.') for v, (vmin, vmax) in kwargs.items(): if vmin is not None: diff --git a/pyxlma/plot/xlma.py b/pyxlma/plot/xlma.py index f2bb823..984ec27 100644 --- a/pyxlma/plot/xlma.py +++ b/pyxlma/plot/xlma.py @@ -2,6 +2,7 @@ import matplotlib.pyplot as plt import matplotlib.dates as md import datetime as dt +import pandas as pd from matplotlib.ticker import Formatter, FormatStrFormatter, MaxNLocator import cartopy @@ -145,7 +146,7 @@ def setup_figure(self, **kwargs): from pandas.plotting import register_matplotlib_converters register_matplotlib_converters() try: - self.datetime = self.data.event_time.to_dataframe().event_time + self.datetime = pd.Series(self.data.event_time.data.flatten()).dt.to_pydatetime() except: self.data_exists=False self.datetime = [self.stime,self.stime+dt.timedelta(minutes=10)] diff --git a/pyxlma/plot/xlma_base_plot.py b/pyxlma/plot/xlma_base_plot.py index 92ec0d5..c2f14b7 100644 --- a/pyxlma/plot/xlma_base_plot.py +++ b/pyxlma/plot/xlma_base_plot.py @@ -181,7 +181,8 @@ def plot(self, **kwargs): # Plan view if self.bkgmap==True: - self.ax_plan.add_feature(COUNTIES, facecolor='none', edgecolor='gray') + if COUNTIES != None: + self.ax_plan.add_feature(COUNTIES, facecolor='none', edgecolor='gray') self.ax_plan.add_feature(cfeature.BORDERS) self.ax_plan.add_feature(cfeature.STATES.with_scale('10m')) self.ax_plan.set_xlabel('Longitude (degrees)') diff --git a/pyxlma/plot/xlma_plot_feature.py b/pyxlma/plot/xlma_plot_feature.py index 6eea864..8b07e43 100644 --- a/pyxlma/plot/xlma_plot_feature.py +++ b/pyxlma/plot/xlma_plot_feature.py @@ -67,19 +67,19 @@ def plot_points(bk_plot, lon_data, lat_data, alt_data, time_data, # before **kwargs was added to the function call, the following arguments # were specified as keywords separately. This allows backwards compatibility: if plot_cmap is None: - plot_cmap = kwargs.pop('cmap', kwargs.pop('plot_cmap', None)) + plot_cmap = kwargs.pop('cmap', plot_cmap) if plot_s is None: - plot_s = kwargs.pop('s', kwargs.pop('plot_s', None)) + plot_s = kwargs.pop('s', plot_s) if plot_vmin is None: - plot_vmin = kwargs.pop('vmin', kwargs.pop('plot_vmin', None)) + plot_vmin = kwargs.pop('vmin', plot_vmin) if plot_vmax is None: - plot_vmax = kwargs.pop('vmax', kwargs.pop('plot_vmax', None)) + plot_vmax = kwargs.pop('vmax', plot_vmax) if plot_c is None: - plot_c = kwargs.pop('c', kwargs.pop('plot_c', None)) + plot_c = kwargs.pop('c', plot_c) if edge_color == 'face': - edge_color = kwargs.pop('edgecolors', kwargs.pop('edge_color', 'face')) + edge_color = kwargs.pop('edgecolors', edge_color) if edge_width == 0: - edge_width = kwargs.pop('linewidths', kwargs.pop('edge_width', 0)) + edge_width = kwargs.pop('linewidths', edge_width) art_plan = bk_plot.ax_plan.scatter(lon_data, lat_data, c=plot_c,vmin=plot_vmin, vmax=plot_vmax, cmap=plot_cmap, @@ -119,7 +119,7 @@ def plot_3d_grid(bk_plot, xedges, yedges, zedges, tedges, """ - plot_cmap = kwargs.pop('cmap', kwargs.pop('plot_cmap', None)) + plot_cmap = kwargs.pop('cmap', plot_cmap) plot_vmin = kwargs.pop('vmin', 0) alt_lon[alt_lon==0]=np.nan diff --git a/tests/test_base_plot.py b/tests/test_base_plot.py new file mode 100644 index 0000000..7d845a8 --- /dev/null +++ b/tests/test_base_plot.py @@ -0,0 +1,124 @@ +import pytest +import xarray as xr +from pyxlma.plot.xlma_base_plot import * +from pyxlma.plot.xlma_plot_feature import * +from pyxlma.lmalib.grid import * +import datetime as dt +import pandas as pd +import matplotlib.dates as md + +@pytest.mark.mpl_image_compare +def test_blank_plot(): + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=True, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + return bk_plot.fig + +@pytest.mark.mpl_image_compare +def test_blank_plot_labeled(): + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=True, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + subplot_labels(bk_plot) + return bk_plot.fig + +@pytest.mark.mpl_image_compare +def test_plot_feature_plot_points_positional(): + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=True, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + times = pd.Series(dataset.event_time.data.flatten()) + vmin, vmax, colors = color_by_time(pd.to_datetime(times), (start_time, end_time)) + plot_points(bk_plot, dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data/1000, + dataset.event_time.data, 'rainbow', 5, vmin, vmax, colors, 'k', 0.1, True) + return bk_plot.fig + +@pytest.mark.mpl_image_compare +def test_plot_feature_plot_points_old_kw(): + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=True, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + times = pd.Series(dataset.event_time.data.flatten()) + vmin, vmax, colors = color_by_time(pd.to_datetime(times), (start_time, end_time)) + plot_points(bk_plot, dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data/1000, + dataset.event_time.data, plot_cmap='rainbow', plot_s=5, plot_vmin=vmin, plot_vmax=vmax, + plot_c=colors, edge_color='k', edge_width=0.1, add_to_histogram=True) + return bk_plot.fig + +@pytest.mark.mpl_image_compare +def test_plot_feature_plot_points_new_kw(): + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=True, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + times = pd.Series(dataset.event_time.data.flatten()) + vmin, vmax, colors = color_by_time(pd.to_datetime(times), (start_time, end_time)) + plot_points(bk_plot, dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data/1000, + dataset.event_time.data, cmap='rainbow', s=5, vmin=vmin, vmax=vmax, + c=colors, edgecolors='k', linewidths=0.1, add_to_histogram=True) + return bk_plot.fig + + +@pytest.mark.mpl_image_compare +def test_plot_feature_plot_points_new_kw_no_bkmap(): + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=False, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + times = pd.Series(dataset.event_time.data.flatten()) + vmin, vmax, colors = color_by_time(pd.to_datetime(times), (start_time, end_time)) + plot_points(bk_plot, dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data/1000, + dataset.event_time.data, cmap='rainbow', s=5, vmin=vmin, vmax=vmax, + c=colors, edgecolors='k', linewidths=0.1, add_to_histogram=True) + return bk_plot.fig + + +@pytest.mark.mpl_image_compare +def test_plot_feature_plot_3d_grid(): + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + x_edges = np.linspace(-103.5, -99.5, 100) + y_edges = np.linspace(31.5, 35.5, 100) + z_edges = np.linspace(0, 20, 100) + t_edges = md.date2num(pd.date_range(start='2023-12-24T00:57:00', end='2023-12-24T00:58:00', periods=7).values) + histograms = setup_hist(dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data/1000, + dataset.event_time.data, x_edges, y_edges, z_edges, t_edges) + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=True, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + + plot_3d_grid(bk_plot, x_edges, y_edges, z_edges, t_edges, *histograms, dataset.event_altitude.data/1000, cmap='plasma') + return bk_plot.fig + +@pytest.mark.mpl_image_compare +def test_plot_feature_plot_3d_grid_old_kw(): + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + x_edges = np.linspace(-103.5, -99.5, 100) + y_edges = np.linspace(31.5, 35.5, 100) + z_edges = np.linspace(0, 20, 100) + t_edges = md.date2num(pd.date_range(start='2023-12-24T00:57:00', end='2023-12-24T00:58:00', periods=7).values) + histograms = setup_hist(dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data/1000, + dataset.event_time.data, x_edges, y_edges, z_edges, t_edges) + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=True, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + + plot_3d_grid(bk_plot, x_edges, y_edges, z_edges, t_edges, *histograms, dataset.event_altitude.data/1000, plot_cmap='plasma') + return bk_plot.fig + + +@pytest.mark.mpl_image_compare +def test_plot_feature_inset_view(): + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + end_time = start_time + dt.timedelta(seconds=60) + bk_plot = BlankPlot(start_time, bkgmap=True, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], tlim=[start_time, end_time], title='XLMA Test Plot') + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + times = pd.Series(dataset.event_time.data.flatten()) + vmin, vmax, colors = color_by_time(pd.to_datetime(times), (start_time, end_time)) + plot_points(bk_plot, dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data/1000, + dataset.event_time.data, cmap='rainbow', s=5, vmin=vmin, vmax=vmax, + c=colors, edgecolors='k', linewidths=0.1, add_to_histogram=True) + inset_view(bk_plot, dataset.event_longitude.data, dataset.event_latitude.data, + [-102.75, -102.25], [32, 32.5], .01, .01) + return bk_plot.fig \ No newline at end of file diff --git a/tests/test_coords.py b/tests/test_coords.py new file mode 100644 index 0000000..b609342 --- /dev/null +++ b/tests/test_coords.py @@ -0,0 +1,153 @@ +from pyxlma.coords import * +import pytest +import numpy as np +from sklearn.neighbors import KDTree + +test_lats = np.array([33.5, 1.0, 0.0, 0.0, 0.0, 10.0, -10.0, 33.606968]) +test_lons = np.array([-101.5, -75.0, -85.0, -65.0, -75.0, -75.0, -75.0, -101.822625]) +test_alts = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 984.0]) + +test_ecef_X = np.array([-1061448.75418035, 1650533.58831094, 555891.26758132, + 2695517.17208404, 1650783.32787306, 1625868.32721344, + 1625868.32721344, -1089633.44245767]) +test_ecef_Y = np.array([-5217187.30723133, -6159875.21117539, -6353866.26310279, + -5780555.22988658, -6160807.25190988, -6067823.20357756, + -6067823.20357756, -5205511.43302535]) +test_ecef_Z = np.array([3500334.28802236, 110568.77482457, 0, 0, + 0, 1100248.54773536, -1100248.54773536, 3510766.26631805]) + +def test_geographic(): + geosys = GeographicSystem() + ecef_coords = geosys.toECEF(test_lons, test_lats, test_alts) + lons, lats, alts = geosys.fromECEF(*ecef_coords) + + assert np.allclose(ecef_coords[0], test_ecef_X) + assert np.allclose(ecef_coords[1], test_ecef_Y) + assert np.allclose(ecef_coords[2], test_ecef_Z) + assert np.allclose(lons, test_lons) + assert np.allclose(lats, test_lats) + assert np.allclose(alts, test_alts) + +def test_geographic_one_point(): + geosys = GeographicSystem() + ecef_coords = geosys.toECEF(np.atleast_1d(test_lons[-1]), np.atleast_1d(test_lats[-1]), np.atleast_1d(test_alts[-1])) + print(len(np.atleast_1d(test_lons[-1]).shape)) + lons, lats, alts = geosys.fromECEF(*ecef_coords) + + assert np.allclose(ecef_coords[0], test_ecef_X[-1]) + assert np.allclose(ecef_coords[1], test_ecef_Y[-1]) + assert np.allclose(ecef_coords[2], test_ecef_Z[-1]) + assert np.allclose(lons[0], test_lons[-1]) + assert np.allclose(lats[0], test_lats[-1]) + assert np.allclose(alts[0], test_alts[-1]) + +def test_geographic_custom_r_both(): + geosys = GeographicSystem(r_equator=6378.137, r_pole=6356.752) + ecef_coords = geosys.toECEF(test_lons, test_lats, test_alts) + lons, lats, alts = geosys.fromECEF(*ecef_coords) + assert np.allclose(lons, test_lons) + assert np.allclose(lats, test_lats) + assert np.allclose(alts, test_alts) + +def test_geographic_custom_r_eq(): + geosys = GeographicSystem(r_equator=6378.137) + ecef_coords = geosys.toECEF(test_lons, test_lats, test_alts) + lons, lats, alts = geosys.fromECEF(*ecef_coords) + assert np.allclose(lons, test_lons) + assert np.allclose(lats, test_lats) + assert np.allclose(alts, test_alts) + +def test_geographic_custom_r_pole(): + geosys = GeographicSystem(r_pole=6356.752) + ecef_coords = geosys.toECEF(test_lons, test_lats, test_alts) + lons, lats, alts = geosys.fromECEF(*ecef_coords) + assert np.allclose(lons, test_lons) + assert np.allclose(lats, test_lats) + assert np.allclose(alts, test_alts) + +def test_equidistant_cylindrical(): + eqsys = MapProjection() + ecef_coords = eqsys.toECEF(0, 0, 0) + x, y, z = eqsys.fromECEF(*ecef_coords) + assert np.allclose(x, 0) + assert np.allclose(y, 0) + assert np.allclose(z, 0) + +def test_equidistant_cylindrical_custom_point(): + eqsys = MapProjection(ctrLat=test_lats[-1], ctrLon=test_lons[-1]) + ecef_coords = eqsys.toECEF(0, 0, 0) + x, y, z = eqsys.fromECEF(*ecef_coords) + assert np.allclose(x, 0) + assert np.allclose(y, 0) + assert np.allclose(z, 0) + + +# def test_px_grid(): +# lon = np.arange(-105, -99.9, 0.5) +# x_coord = np.arange(0, len(lon)) +# lat = np.arange(30, 35.1, 0.5) +# y_coord = np.arange(0, len(lat)) +# lon, lat = np.meshgrid(lon, lat) +# pxgrid = PixelGrid(lon, lat, KDTree, x_coord, y_coord) +# ecef_coords = pxgrid.toECEF(np.array(7), np.array(7), np.array(0)) +# x, y, z = pxgrid.fromECEF(*ecef_coords) + +# assert np.allclose(ecef_coords[0], test_ecef_X[0]) +# assert np.allclose(ecef_coords[1], test_ecef_Y[0]) +# assert np.allclose(ecef_coords[2], test_ecef_Z[0]) + +# assert np.allclose(x, 7) +# assert np.allclose(y, 7) +# assert np.allclose(z, 0) + +def test_satellite_system(): + sat = GeostationaryFixedGridSystem(subsat_lon=75.2) + ecef_coords = sat.toECEF(0.01, 0.01, 0.01) + x, y, z = sat.fromECEF(*ecef_coords) + + assert np.allclose(x, 0.01) + assert np.allclose(y, 0.01) + assert np.allclose(z, 0.01) + + +def test_radar_system_height(): + ADRAD_rcs = RadarCoordinateSystem(30.6177, -96.3365, 114) + tornado_ground_range, beam_height_agl = ADRAD_rcs.getGroundRangeHeight(17150, 1.4) + assert np.allclose(tornado_ground_range, 17144.013390611748) + assert np.allclose(beam_height_agl, 550.2784673999995) + +def test_radar_system_elevation(): + ADRAD_rcs = RadarCoordinateSystem(30.6177, -96.3365, 114) + tornado_slant_range, radar_elevation = ADRAD_rcs.getSlantRangeElevation(17144.013390611748, 550.2784673999995) + print(tornado_slant_range, radar_elevation) + assert np.allclose(tornado_slant_range, 17150) + assert np.allclose(radar_elevation, 1.4) + +def test_radar_system_lla(): + ADRAD_rcs = RadarCoordinateSystem(30.6177, -96.3365, 114) + tornado_lon, tornado_lat, tornado_alt = ADRAD_rcs.toLonLatAlt(np.atleast_1d(17150), np.atleast_1d(228), np.atleast_1d(1.4)) + assert np.allclose(tornado_lat, 30.51415605367721) + assert np.allclose(tornado_lon, -96.46923405085701) + assert np.allclose(tornado_alt, 550.2784674) + +def test_radar_system_ecef(): + ADRAD_rcs = RadarCoordinateSystem(30.6177, -96.3365, 114) + tornado_x, tornado_y, tornado_z = ADRAD_rcs.toECEF(np.atleast_1d(17150), np.atleast_1d(228), np.atleast_1d(1.4)) + tornado_r, tornado_az, tornado_el = ADRAD_rcs.fromECEF(tornado_x, tornado_y, tornado_z) + assert np.allclose(tornado_r, 17150) + assert np.allclose(tornado_az, 228) + assert np.allclose(tornado_el, 1.4) + +def test_tpcs(): + tpcs = TangentPlaneCartesianSystem(ctrLat=test_lats[-1], ctrLon=test_lons[-1], ctrAlt=test_alts[-1]) + ecef_coords = tpcs.toECEF(100, 100, 100) + x, y, z = tpcs.fromECEF(*ecef_coords) + assert np.allclose(x, 100) + assert np.allclose(y, 100) + assert np.allclose(z, 100) + +def test_tpcs_local(): + tpcs = TangentPlaneCartesianSystem(ctrLat=test_lats[-1], ctrLon=test_lons[-1], ctrAlt=test_alts[-1]) + ecef_coords = tpcs.toLocal(np.array([[100, 100, 100], [200, 200, 200], [300, 300, 300]])) + local_coords = tpcs.fromLocal(ecef_coords) + assert np.allclose(local_coords, np.array([[100, 100, 100], [200, 200, 200], [300, 300, 300]])) diff --git a/tests/test_flash.py b/tests/test_flash.py new file mode 100644 index 0000000..c20968b --- /dev/null +++ b/tests/test_flash.py @@ -0,0 +1,79 @@ +"""Test functionality of pyxlma.lmalib.flash""" + +import xarray as xr +import numpy as np +import pytest +from pyxlma.lmalib.flash.cluster import cluster_flashes +from pyxlma.lmalib.flash.properties import * + + +def compare_dataarrays(tocheck, truth, var): + """Compare two dataarrays""" + if truth[var].data.dtype == 'datetime64[ns]' or truth[var].data.dtype == 'timedelta64[ns]': + if tocheck[var].data.dtype == 'float64': + truth[var].data = truth[var].data.astype(float)/1e9 + np.testing.assert_allclose(tocheck[var].data.astype(float), truth[var].data.astype(float)) + else: + np.testing.assert_allclose(tocheck[var].data, truth[var].data) + + +def test_cluster_flashes(): + """Test clustering of flashes""" + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + clustered = cluster_flashes(dataset) + truth = xr.open_dataset('tests/truth/lma_netcdf/lma_clustered.nc') + for var in truth.data_vars: + compare_dataarrays(clustered, truth, var) + + +def test_flash_stats(): + """Test calculation of flash statistics""" + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + stats = flash_stats(cluster_flashes(dataset)) + truth = xr.open_dataset('tests/truth/lma_netcdf/lma_stats.nc') + for var in truth.data_vars: + compare_dataarrays(stats, truth, var) + + +def test_filter_flashes(): + """Test filtering of flashes""" + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma_stats.nc') + filtered = filter_flashes(dataset, flash_event_count=(100, 500)) + assert np.min(filtered.flash_event_count.data) >= 100 + assert np.max(filtered.flash_event_count.data) <= 500 + + truth = xr.open_dataset('tests/truth/lma_netcdf/lma_filtered.nc') + + for var in truth.data_vars: + compare_dataarrays(filtered, truth, var) + + +def test_flilter_flashes_no_prune(): + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma_stats.nc') + filtered = filter_flashes(dataset, flash_event_count=(100, 500), prune=False) + assert np.all(filtered.event_id.data == dataset.event_id.data) + assert np.min(filtered.flash_event_count.data) >= 100 + assert np.max(filtered.flash_event_count.data) <= 500 + + +def test_filter_no_stats(): + """"Test filtering of flashes without flash statistics""" + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + dataset = cluster_flashes(dataset) + with pytest.raises(ValueError, match='Before filtering a dataset by flash properties, call flash_stats on the dataset to compute flash properties.'): + filter_flashes(dataset, flash_event_count=(100, 500)) + + +def test_event_area(): + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + x, y, z = local_cartesian(dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data, + dataset.network_center_longitude.data, dataset.network_center_latitude.data, dataset.network_center_altitude.data) + assert np.isclose(event_hull_area(x, y, z), 5491450433206.501) + + +def test_event_volume(): + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + x, y, z = local_cartesian(dataset.event_longitude.data, dataset.event_latitude.data, dataset.event_altitude.data, + dataset.network_center_longitude.data, dataset.network_center_latitude.data, dataset.network_center_altitude.data) + assert np.isclose(event_hull_volume(x[0:10], y[0:10], z[0:10]), 56753729942.624825) + \ No newline at end of file diff --git a/tests/test_grid.py b/tests/test_grid.py new file mode 100644 index 0000000..f414aac --- /dev/null +++ b/tests/test_grid.py @@ -0,0 +1,69 @@ +import xarray as xr +from pyxlma.lmalib.grid import * +from tests.test_flash import compare_dataarrays + + +def test_create_regular_grid(): + """Test creation of regular grid""" + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma_stats.nc') + grid_range = 0.5 + grid_h_res = 0.1 + grid_height = 20 + grid_v_res = 1 + lon_range = (dataset.network_center_longitude - grid_range, dataset.network_center_longitude + grid_range, grid_h_res) + lat_range = (dataset.network_center_latitude - grid_range, dataset.network_center_latitude + grid_range, grid_h_res) + alt_range = (0, grid_height, grid_v_res) + time_range = (dataset.event_time.data.min(), dataset.event_time.data.max(), np.timedelta64(1, 'm')) + grid_edge_ranges ={ + 'grid_longitude_edge':lon_range, + 'grid_latitude_edge':lat_range, + 'grid_altitude_edge':alt_range, + 'grid_time_edge':time_range, + } + grid_center_names ={ + 'grid_longitude_edge':'grid_longitude', + 'grid_latitude_edge':'grid_latitude', + 'grid_altitude_edge':'grid_altitude', + 'grid_time_edge':'grid_time', + } + empty_grid = create_regular_grid(grid_edge_ranges, grid_center_names) + xr.testing.assert_equal(empty_grid, xr.open_dataset('tests/truth/lma_netcdf/empty_grid.nc')) + +def test_assign_regular_bins(): + """Test assigning lightning data to regular bins""" + empty_grid = xr.open_dataset('tests/truth/lma_netcdf/empty_grid.nc') + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma_stats.nc') + event_coord_names = { + 'event_longitude':'grid_longitude_edge', + 'event_latitude':'grid_latitude_edge', + 'event_altitude':'grid_altitude_edge', + 'event_time':'grid_time_edge', + } + binned_grid = assign_regular_bins(empty_grid, dataset, event_coord_names, append_indices=True) + truth = xr.open_dataset('tests/truth/lma_netcdf/binned_grid.nc') + + for var in truth.data_vars: + compare_dataarrays(binned_grid, truth, var) + + +def test_events_to_grid(): + """Test gridding lightning data""" + empty_grid = xr.open_dataset('tests/truth/lma_netcdf/empty_grid.nc') + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma_stats.nc') + event_coord_names = { + 'event_longitude':'grid_longitude_edge', + 'event_latitude':'grid_latitude_edge', + 'event_altitude':'grid_altitude_edge', + 'event_time':'grid_time_edge', + } + binned_grid = assign_regular_bins(empty_grid, dataset, event_coord_names, append_indices=True) + + grid_spatial_coords=('grid_time', 'grid_altitude', 'grid_latitude', 'grid_longitude') + event_spatial_vars = ('event_time', 'event_altitude', 'event_latitude', 'event_longitude') + + gridded_lma = events_to_grid(binned_grid, empty_grid, pixel_id_var='pixel_id', event_spatial_vars=event_spatial_vars, grid_spatial_coords=grid_spatial_coords) + + truth = xr.open_dataset('tests/truth/lma_netcdf/gridded_lma.nc') + + for var in truth.data_vars: + compare_dataarrays(gridded_lma, truth, var) \ No newline at end of file diff --git a/tests/test_interactive.py b/tests/test_interactive.py new file mode 100644 index 0000000..cc88eb9 --- /dev/null +++ b/tests/test_interactive.py @@ -0,0 +1,14 @@ +import pytest +import xarray as xr +import datetime as dt +from pyxlma.plot.interactive import * + +@pytest.mark.mpl_image_compare +def test_interactive(): + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + buffer = 2.5 + xlim = (dataset.network_center_longitude.data-buffer, dataset.network_center_longitude.data+buffer) + ylim = (dataset.network_center_latitude.data-buffer, dataset.network_center_latitude.data+buffer) + zlim = (0, 20) + interact = InteractiveLMAPlot(dataset, clon=dataset.network_center_longitude, clat=dataset.network_center_latitude, xlim=xlim, ylim=ylim, zlim=zlim, plot_cmap='rainbow') + return interact.lma_plot.fig diff --git a/tests/test_io.py b/tests/test_io.py index 1b7bb86..66c3d1e 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -1,4 +1,4 @@ -"""Test functionality of pyxlma.io""" +"""Test functionality of pyxlma.lmalib.io""" from os import listdir from datetime import datetime as dt @@ -8,8 +8,8 @@ def test_read_mf_dataset(): """Test reading of multiple .dat.gz files into a single lma dataset""" - files_to_read = listdir('examples/data/lma/') - files_to_read = ['examples/data/lma/'+file for file in files_to_read] + files_to_read = listdir('examples/data/') + files_to_read = ['examples/data/'+file for file in files_to_read] dataset, start_date = lma_read.dataset(files_to_read) assert start_date == dt(2023, 12, 24, 0, 57, 1) - assert dataset == xr.open_dataset('examples/data/lma_netcdf/lma.nc') + assert dataset == xr.open_dataset('tests/truth/lma_netcdf/lma.nc') diff --git a/tests/test_plot_feature.py b/tests/test_plot_feature.py new file mode 100644 index 0000000..5cdced9 --- /dev/null +++ b/tests/test_plot_feature.py @@ -0,0 +1,37 @@ +from pyxlma.plot.xlma_plot_feature import * +import xarray as xr +import numpy as np +from datetime import datetime as dt + +def test_subset(): + lma = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + lon_subset, lat_subset, alt_subset, time_subset, selection = subset(lma.event_longitude.data, lma.event_latitude.data, lma.event_altitude.data, + lma.event_time.data, lma.event_chi2.data, lma.event_stations.data, + (-101.7, -101.4), (33.4, 34.8), (0, 3000), + (np.datetime64('2023-12-24T00:57:00'), np.datetime64('2023-12-24T00:57:10')), 1, 8) + assert np.allclose(lon_subset, np.array([-101.59106, -101.598236, -101.59939, -101.59875, -101.601425, -101.60555, -101.60554, -101.60838, -101.60368, -101.62052])) + assert np.allclose(lat_subset, np.array([33.68953, 33.684334, 33.68242, 33.67924, 33.678104, 33.676983, 33.675335, 33.677456, 33.67102, 33.666958])) + assert np.allclose(alt_subset, np.array([2974.67, 2986.99, 2936.03, 2920.17, 2797.01, 2933.09, 2659.97, 2886.72, 2716.22, 2943.72])) + assert np.allclose(time_subset.astype(float), np.array(['2023-12-24T00:57:07.731986515', '2023-12-24T00:57:07.800978747', '2023-12-24T00:57:07.803362858', '2023-12-24T00:57:07.805963963', + '2023-12-24T00:57:07.806720943', '2023-12-24T00:57:07.809493631', '2023-12-24T00:57:07.810448100', '2023-12-24T00:57:07.811465266', + '2023-12-24T00:57:07.814960674', '2023-12-24T00:57:07.826344209']).astype(np.datetime64).astype(float)) + assert np.sum(selection) == 10 + +def test_color_by_time_datetime(): + some_datetimes = np.array([dt(2021, 4, 9, 1, 51, 0), dt(2021, 4, 9, 1, 52, 0), dt(2021, 4, 9, 1, 53, 0), dt(2021, 4, 9, 1, 54, 0), dt(2021, 4, 9, 1, 59, 0)]) + limits = [dt(2021, 4, 9, 1, 50, 0), dt(2021, 4, 9, 2, 0, 0)] + vmin, vmax, colors = color_by_time(some_datetimes, limits) + assert vmin == 0 + assert vmax == 540 + assert np.allclose(colors, np.array([0, 60, 120, 180, 480])) + + +def test_setup_hist(): + lma = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + alt_lon, alt_lat, alt_time, lat_lon = setup_hist(lma.event_longitude.data, lma.event_latitude.data, lma.event_altitude.data, + lma.event_time.data, 2, 2, 2, 2) + + assert np.allclose(alt_lon, np.array([[21082, 1], [0, 1]])) + assert np.allclose(alt_lat, np.array([[5, 21077], [1, 1]])) + assert np.allclose(alt_time, np.array([[9991, 1], [11091, 1]])) + assert np.allclose(lat_lon, np.array([[6, 21077], [0, 1]])) diff --git a/tests/test_xlma_plot.py b/tests/test_xlma_plot.py new file mode 100644 index 0000000..83ce099 --- /dev/null +++ b/tests/test_xlma_plot.py @@ -0,0 +1,13 @@ +import pytest +from pyxlma.plot.xlma import * +import xarray as xr +import datetime as dt +import pandas as pd + +@pytest.mark.mpl_image_compare +def test_xlma_plot(): + dataset = xr.open_dataset('tests/truth/lma_netcdf/lma.nc') + start_time = dt.datetime(2023, 12, 24, 0, 57, 0, 0) + print(pd.Series(dataset.event_time.data.flatten())) + xlma = XlmaPlot(dataset, start_time, True, True, 'xarray', False, xlim=[-103.5, -99.5], ylim=[31.5, 35.5], zlim=[0, 20], cmap='rainbow', chi2=1) + return xlma.fig \ No newline at end of file diff --git a/tests/truth/images/test_blank_plot.png b/tests/truth/images/test_blank_plot.png new file mode 100644 index 0000000..40070e2 Binary files /dev/null and b/tests/truth/images/test_blank_plot.png differ diff --git a/tests/truth/images/test_blank_plot_labeled.png b/tests/truth/images/test_blank_plot_labeled.png new file mode 100644 index 0000000..765b029 Binary files /dev/null and b/tests/truth/images/test_blank_plot_labeled.png differ diff --git a/tests/truth/images/test_interactive.png b/tests/truth/images/test_interactive.png new file mode 100644 index 0000000..6bae648 Binary files /dev/null and b/tests/truth/images/test_interactive.png differ diff --git a/tests/truth/images/test_plot_feature_inset_view.png b/tests/truth/images/test_plot_feature_inset_view.png new file mode 100644 index 0000000..df33fb6 Binary files /dev/null and b/tests/truth/images/test_plot_feature_inset_view.png differ diff --git a/tests/truth/images/test_plot_feature_plot_3d_grid.png b/tests/truth/images/test_plot_feature_plot_3d_grid.png new file mode 100644 index 0000000..8934c29 Binary files /dev/null and b/tests/truth/images/test_plot_feature_plot_3d_grid.png differ diff --git a/tests/truth/images/test_plot_feature_plot_3d_grid_old_kw.png b/tests/truth/images/test_plot_feature_plot_3d_grid_old_kw.png new file mode 100644 index 0000000..8934c29 Binary files /dev/null and b/tests/truth/images/test_plot_feature_plot_3d_grid_old_kw.png differ diff --git a/tests/truth/images/test_plot_feature_plot_points_new_kw.png b/tests/truth/images/test_plot_feature_plot_points_new_kw.png new file mode 100644 index 0000000..9602645 Binary files /dev/null and b/tests/truth/images/test_plot_feature_plot_points_new_kw.png differ diff --git a/tests/truth/images/test_plot_feature_plot_points_new_kw_no_bkmap.png b/tests/truth/images/test_plot_feature_plot_points_new_kw_no_bkmap.png new file mode 100644 index 0000000..824e106 Binary files /dev/null and b/tests/truth/images/test_plot_feature_plot_points_new_kw_no_bkmap.png differ diff --git a/tests/truth/images/test_plot_feature_plot_points_old_kw.png b/tests/truth/images/test_plot_feature_plot_points_old_kw.png new file mode 100644 index 0000000..9602645 Binary files /dev/null and b/tests/truth/images/test_plot_feature_plot_points_old_kw.png differ diff --git a/tests/truth/images/test_plot_feature_plot_points_positional.png b/tests/truth/images/test_plot_feature_plot_points_positional.png new file mode 100644 index 0000000..9602645 Binary files /dev/null and b/tests/truth/images/test_plot_feature_plot_points_positional.png differ diff --git a/tests/truth/images/test_xlma_plot.png b/tests/truth/images/test_xlma_plot.png new file mode 100644 index 0000000..4e3a8d6 Binary files /dev/null and b/tests/truth/images/test_xlma_plot.png differ diff --git a/tests/truth/lma_netcdf/binned_grid.nc b/tests/truth/lma_netcdf/binned_grid.nc new file mode 100644 index 0000000..172df29 Binary files /dev/null and b/tests/truth/lma_netcdf/binned_grid.nc differ diff --git a/tests/truth/lma_netcdf/empty_grid.nc b/tests/truth/lma_netcdf/empty_grid.nc new file mode 100644 index 0000000..21e2b41 Binary files /dev/null and b/tests/truth/lma_netcdf/empty_grid.nc differ diff --git a/tests/truth/lma_netcdf/gridded_lma.nc b/tests/truth/lma_netcdf/gridded_lma.nc new file mode 100644 index 0000000..181afde Binary files /dev/null and b/tests/truth/lma_netcdf/gridded_lma.nc differ diff --git a/tests/truth/lma_netcdf/lma.nc b/tests/truth/lma_netcdf/lma.nc new file mode 100644 index 0000000..998088e Binary files /dev/null and b/tests/truth/lma_netcdf/lma.nc differ diff --git a/tests/truth/lma_netcdf/lma_clustered.nc b/tests/truth/lma_netcdf/lma_clustered.nc new file mode 100644 index 0000000..d543dcb Binary files /dev/null and b/tests/truth/lma_netcdf/lma_clustered.nc differ diff --git a/tests/truth/lma_netcdf/lma_filtered.nc b/tests/truth/lma_netcdf/lma_filtered.nc new file mode 100644 index 0000000..dcf9598 Binary files /dev/null and b/tests/truth/lma_netcdf/lma_filtered.nc differ diff --git a/tests/truth/lma_netcdf/lma_stats.nc b/tests/truth/lma_netcdf/lma_stats.nc new file mode 100644 index 0000000..24b4072 Binary files /dev/null and b/tests/truth/lma_netcdf/lma_stats.nc differ