├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .github
└── workflows
│ ├── anaconda-build.yaml
│ ├── pypi-build.yaml
│ └── unittest.yaml
├── .gitignore
├── .gitpod.Dockerfile
├── .gitpod.yml
├── LICENSE
├── MANIFEST.in
├── README.md
├── README.rst
├── docs
├── Makefile
├── Pytplot HTML Tutorial.ipynb
├── make.bat
├── pytplot_tutorial.html
├── source
│ ├── _images
│ │ ├── 2d.png
│ │ ├── 3d.png
│ │ ├── altitude.html
│ │ ├── extra_x_axes.png
│ │ ├── gui.png
│ │ ├── interactivity.png
│ │ ├── map.html
│ │ ├── sample.html
│ │ ├── sample.png
│ │ ├── spec_slicer.png
│ │ └── specslicer.html
│ ├── conf.py
│ ├── helper_functions.rst
│ ├── index.rst
│ ├── interactivity.rst
│ ├── introduction.rst
│ ├── linking.rst
│ ├── math_routines.rst
│ ├── options.rst
│ ├── plotting.rst
│ ├── reading_in_data.rst
│ ├── tplot_options.rst
│ └── tplot_variables.rst
└── test_data.tplot
├── meta.yaml
├── pyqtgraphtestertemp.py
├── pytplot
├── AncillaryPlots
│ ├── __init__.py
│ ├── position_mars_2d.py
│ ├── position_mars_3d.py
│ └── spec_slicer.py
├── HTMLPlotter
│ ├── CustomModels
│ │ ├── __init__.py
│ │ ├── colorbarsidetitle.py
│ │ └── timestamp.py
│ ├── TVarFigure1D.py
│ ├── TVarFigureAlt.py
│ ├── TVarFigureMap.py
│ ├── TVarFigureSpec.py
│ ├── __init__.py
│ └── generate.py
├── QtPlotter
│ ├── CustomAxis
│ │ ├── AxisItem.py
│ │ ├── BlankAxis.py
│ │ ├── DateAxis.py
│ │ ├── NonLinearAxis.py
│ │ └── __init__.py
│ ├── CustomImage
│ │ ├── ColorbarImage.py
│ │ ├── UpdatingImage.py
│ │ └── __init__.py
│ ├── CustomLegend
│ │ ├── CustomLegend.py
│ │ └── __init__.py
│ ├── CustomLinearRegionItem
│ │ └── CustomLinearRegionItem.py
│ ├── CustomViewBox
│ │ ├── CustomVB.py
│ │ ├── NoPaddingPlot.py
│ │ └── __init__.py
│ ├── PyTPlot_Exporter.py
│ ├── TVarFigure1D.py
│ ├── TVarFigureAlt.py
│ ├── TVarFigureAxisOnly.py
│ ├── TVarFigureMap.py
│ ├── TVarFigureSpec.py
│ ├── __init__.py
│ └── generate.py
├── __init__.py
├── del_data.py
├── exporters
│ ├── __init__.py
│ ├── tplot_ascii.py
│ └── tplot_save.py
├── get_data.py
├── get_timespan.py
├── get_ylimits.py
├── importers
│ ├── __init__.py
│ ├── cdf_to_tplot.py
│ ├── netcdf_to_tplot.py
│ ├── sts_to_tplot.py
│ └── tplot_restore.py
├── link.py
├── options.py
├── replace_data.py
├── sampledata
│ └── test_data.tplot
├── spedas_colorbar.py
├── store_data.py
├── timebar.py
├── timespan.py
├── timestamp.py
├── tlimit.py
├── tplot.py
├── tplot_copy.py
├── tplot_math
│ ├── __init__.py
│ ├── add.py
│ ├── add_across.py
│ ├── avg_res_data.py
│ ├── clip.py
│ ├── crop.py
│ ├── deflag.py
│ ├── degap.py
│ ├── derive.py
│ ├── divide.py
│ ├── examples.py
│ ├── flatten.py
│ ├── interp_nan.py
│ ├── join_vec.py
│ ├── multiply.py
│ ├── pwr_spec.py
│ ├── resample.py
│ ├── spec_mult.py
│ ├── split_vec.py
│ ├── subtract.py
│ └── tinterp.py
├── tplot_names.py
├── tplot_options.py
├── tplot_rename.py
├── tplot_utilities.py
├── version.txt
├── xlim.py
├── ylim.py
└── zlim.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── tests
├── __init__.py
├── test_alt_plot.py
├── test_cdf_to_tplot.py
├── test_maps.py
├── test_netcdf_to_tplot.py
├── test_qt_import.py
├── test_tplot_math.py
└── testfiles
├── g15_xrs_2s_20170619_20170619.nc
├── mvn_euv_l2_bands_20170619_v09_r03.cdf
└── mvn_swe_l2_svyspec_20170619_v04_r04.cdf
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | # OS
2 | ARG VARIANT=bullseye
3 | FROM --platform=linux/amd64 mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT}
4 | RUN sudo apt-get update
5 | RUN sudo apt-get install -y libgtk-3-dev
6 | RUN sudo apt-get install -y libxcb-xinerama0
7 | RUN sudo apt-get install -y libxcb-render-util0
8 | RUN sudo apt-get install -y libxcb-randr0
9 | RUN sudo apt-get install -y libxcb-shape0
10 | RUN sudo apt-get install -y libxcb-icccm4
11 | RUN sudo apt-get install -y libxcb-image0
12 | RUN sudo apt-get install -y libxcb-keysyms1
13 | RUN sudo apt-get install -y libxkbcommon-x11-0
14 | RUN sudo apt-get install -y python3
15 | RUN sudo apt-get install -y python3-pip
16 | RUN pip install pyspedas
17 | RUN pip install numpy>=1.20.0
18 | RUN pip install "bokeh>=1.1,<3.0"
19 | RUN pip install pyqtgraph>=0.11.1
20 | RUN pip install pandas
21 | RUN pip install matplotlib
22 | RUN pip install scipy
23 | RUN pip install cdflib
24 | RUN pip install xarray
25 | RUN pip install pyqt5>=5.15.2
26 | RUN pip install pyqtwebengine>=5.15.2
27 | # The libraries below are not needed for pytplot to work as is, but are listed as requirements for PyQT5
28 | # If things don't work, maybe try installing these
29 | #RUN sudo apt-get install -y libfontconfig1
30 | #RUN sudo apt-get install -y libfreetype6
31 | #RUN sudo apt-get install -y libxext6
32 | #RUN sudo apt-get install -y libxcb1
33 | #RUN sudo apt-get install -y libx11-6
34 | #RUN sudo apt-get install -y libice6
35 | #RUN sudo apt-get install -y libglib2.0-0
36 | #RUN sudo apt-get install -y libpthread-stubs0-dev
37 | #RUN sudo apt-get install -y libsm6
38 | #RUN sudo apt-get install -y libxcb-util-dev
39 | #RUN sudo apt-get install -y libxrender-dev
40 | #RUN sudo apt-get install -y libxcb-render0
41 | #RUN sudo apt-get install -y libxkbcommon0
42 | #RUN sudo apt-get install -y libxcb-sync1
43 | #RUN sudo apt-get install -y libxcb-xfixes0
44 | #RUN sudo apt-get install -y libxcb-shm0
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Hello",
3 | "build": {
4 | "dockerfile": "Dockerfile",
5 | "args": {
6 | "VARIANT": "bullseye"
7 | }
8 | },
9 | "features": {
10 | "ghcr.io/devcontainers/features/desktop-lite:1": {
11 | "version": "latest"
12 | }
13 | },
14 | "forwardPorts": [6080]
15 | }
--------------------------------------------------------------------------------
/.github/workflows/anaconda-build.yaml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Install dependencies
12 | run: |
13 | cd $HOME
14 | # wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
15 | wget https://repo.anaconda.com/miniconda/Miniconda3-py38_4.10.3-Linux-x86_64.sh -O miniconda.sh
16 | chmod +x miniconda.sh
17 | bash miniconda.sh -b -p $HOME/miniconda
18 | export PATH="$HOME/miniconda/bin:$PATH"
19 | sudo apt-get -y install libgl1-mesa-glx
20 | sudo apt-get -y install libsm6 libxrender1 libfontconfig1 libxcomposite-dev libxcursor1 libxi6 libxtst6 libxss1 libxrandr2 libasound2 libegl1
21 | sudo apt-get -y install xvfb
22 | $HOME/miniconda/bin/conda install astropy xarray numpy scipy pyopengl pytest flake8 six coverage bokeh pyqtgraph matplotlib --yes
23 | $HOME/miniconda/bin/conda install pyqt --yes
24 | $HOME/miniconda/bin/conda install pip --yes
25 | $HOME/miniconda/bin/conda install -c anaconda netcdf4 --yes
26 | $HOME/miniconda/bin/conda install -c mavensdc cdflib --yes
27 | $HOME/miniconda/bin/conda install keyring --yes
28 | - name: Run unit tests
29 | run: |
30 | xvfb-run $HOME/miniconda/bin/pytest -rsv $GITHUB_WORKSPACE/tests
31 | cd $GITHUB_WORKSPACE
32 | $HOME/miniconda/bin/python setup.py sdist
33 | - name: Publish to Anaconda
34 | env:
35 | ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }}
36 | if: startsWith(github.event.ref, 'refs/tags')
37 | run: |
38 | alias python=$HOME/miniconda/bin/python
39 | alias pip=$HOME/miniconda/bin/pip
40 | $HOME/miniconda/bin/pip install twine
41 | $HOME/miniconda/bin/conda install conda-build --yes
42 | $HOME/miniconda/bin/conda install anaconda-client --yes
43 | $HOME/miniconda/bin/conda config --add channels mavensdc
44 | $HOME/miniconda/bin/conda-build --python 3.6 --output-folder . .
45 | $HOME/miniconda/bin/conda convert -p linux-32 linux-64/*.tar.bz2
46 | $HOME/miniconda/bin/conda convert -p win-32 linux-64/*.tar.bz2
47 | $HOME/miniconda/bin/conda convert -p osx-64 linux-64/*.tar.bz2
48 | $HOME/miniconda/bin/conda convert -p win-64 linux-64/*.tar.bz2
49 | $HOME/miniconda/bin/anaconda upload --label main linux-64/*.tar.bz2
50 | $HOME/miniconda/bin/anaconda upload --label main osx-64/*.tar.bz2
51 | $HOME/miniconda/bin/anaconda upload --label main win-64/*.tar.bz2
52 | $HOME/miniconda/bin/anaconda upload --label main win-32/*.tar.bz2
53 | $HOME/miniconda/bin/anaconda upload --label main linux-32/*.tar.bz2
54 | rm -r linux-64
55 | rm -r linux-32
56 | rm -r osx-64
57 | rm -r win-64
58 | rm -r win-32
59 | $HOME/miniconda/bin/conda-build --python 3.7 --output-folder . .
60 | $HOME/miniconda/bin/conda convert -p linux-32 linux-64/*.tar.bz2
61 | $HOME/miniconda/bin/conda convert -p win-32 linux-64/*.tar.bz2
62 | $HOME/miniconda/bin/conda convert -p osx-64 linux-64/*.tar.bz2
63 | $HOME/miniconda/bin/conda convert -p win-64 linux-64/*.tar.bz2
64 | $HOME/miniconda/bin/anaconda upload --label main linux-64/*.tar.bz2
65 | $HOME/miniconda/bin/anaconda upload --label main osx-64/*.tar.bz2
66 | $HOME/miniconda/bin/anaconda upload --label main win-64/*.tar.bz2
67 | $HOME/miniconda/bin/anaconda upload --label main win-32/*.tar.bz2
68 | $HOME/miniconda/bin/anaconda upload --label main linux-32/*.tar.bz2
69 | rm -r linux-64
70 | rm -r linux-32
71 | rm -r osx-64
72 | rm -r win-64
73 | rm -r win-32
74 |
--------------------------------------------------------------------------------
/.github/workflows/pypi-build.yaml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Install dependencies
12 | run: |
13 | cd $HOME
14 | # wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
15 | wget https://repo.anaconda.com/miniconda/Miniconda3-py38_4.10.3-Linux-x86_64.sh -O miniconda.sh
16 | chmod +x miniconda.sh
17 | bash miniconda.sh -b -p $HOME/miniconda
18 | export PATH="$HOME/miniconda/bin:$PATH"
19 | sudo apt-get -y install libgl1-mesa-glx
20 | sudo apt-get -y install libsm6 libxrender1 libfontconfig1 libxcomposite-dev libxcursor1 libxi6 libxtst6 libxss1 libxrandr2 libasound2 libegl1
21 | sudo apt-get -y install xvfb
22 | $HOME/miniconda/bin/conda install astropy xarray numpy scipy pyopengl pytest flake8 six coverage bokeh pyqtgraph matplotlib --yes
23 | $HOME/miniconda/bin/conda install pyqt --yes
24 | $HOME/miniconda/bin/conda install pip --yes
25 | $HOME/miniconda/bin/conda install -c anaconda netcdf4 --yes
26 | $HOME/miniconda/bin/conda install -c mavensdc cdflib --yes
27 | $HOME/miniconda/bin/conda install keyring --yes
28 | - name: Run unit tests
29 | run: |
30 | xvfb-run $HOME/miniconda/bin/pytest -rsv $GITHUB_WORKSPACE/tests
31 | cd $GITHUB_WORKSPACE
32 | $HOME/miniconda/bin/python setup.py sdist
33 | - name: Publish to PyPI
34 | uses: pypa/gh-action-pypi-publish@master
35 | if: startsWith(github.event.ref, 'refs/tags')
36 | with:
37 | password: ${{ secrets.pypi_password }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/unittest.yaml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Install dependencies
12 | run: |
13 | cd $HOME
14 | # wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
15 | wget https://repo.anaconda.com/miniconda/Miniconda3-py38_4.10.3-Linux-x86_64.sh -O miniconda.sh
16 | chmod +x miniconda.sh
17 | bash miniconda.sh -b -p $HOME/miniconda
18 | export PATH="$HOME/miniconda/bin:$PATH"
19 | sudo apt-get -y install libgl1-mesa-glx
20 | sudo apt-get -y install libsm6 libxrender1 libfontconfig1 libxcomposite-dev libxcursor1 libxi6 libxtst6 libxss1 libxrandr2 libasound2 libegl1
21 | sudo apt-get -y install xvfb
22 | $HOME/miniconda/bin/conda install astropy xarray numpy scipy pyopengl pytest flake8 six coverage bokeh pyqtgraph matplotlib --yes
23 | $HOME/miniconda/bin/conda install pyqt --yes
24 | $HOME/miniconda/bin/conda install pip --yes
25 | $HOME/miniconda/bin/conda install -c anaconda netcdf4 --yes
26 | $HOME/miniconda/bin/conda install -c mavensdc cdflib --yes
27 | $HOME/miniconda/bin/conda install keyring --yes
28 | - name: Run unit tests
29 | run: |
30 | xvfb-run $HOME/miniconda/bin/pytest -rsv $GITHUB_WORKSPACE/tests
31 | cd $GITHUB_WORKSPACE
32 | $HOME/miniconda/bin/python setup.py sdist
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.tar
2 | *.pyc
3 | *.tar.gz
4 | *.egg-info/
5 | *.tar.gz
6 | *.tgz
7 | /.project
8 | /.pydevproject
9 | *.ipynb
10 | /*.html
11 | *.zip
12 | *.prefs
13 | pytplot/TESTING.py
14 | pytplot/Testing2.py
15 | pytplot/OTHER_TESTING.py
16 | pytplot/TESTING_2.py
17 | *.tab
18 | pytplot/TESTING.py
19 | pytplot/TESTING.py
20 | pytplot/TESTING.py
21 | .spyproject/
22 | test.py
23 | /testing.py
24 | /3dtesting.py
25 | /testing2.py
26 | .idea/
27 | .eggs/
28 | *.orig
29 | docs/build/*
30 | pydata
31 | maven_data
32 | themis_data
33 | pydata
34 |
35 | # Sublime-github package stores a github token in this file
36 | # https://packagecontrol.io/packages/sublime-github
37 | GitHub.sublime-settings
38 |
39 | .svn/
40 |
41 | # Windows thumbnail cache files
42 | Thumbs.db
43 | ehthumbs.db
44 | ehthumbs_vista.db
45 |
46 | # Dump file
47 | *.stackdump
48 |
49 | # Folder config file
50 | [Dd]esktop.ini
51 |
52 | # Recycle Bin used on file shares
53 | $RECYCLE.BIN/
54 |
55 | # Windows Installer files
56 | *.cab
57 | *.msi
58 | *.msix
59 | *.msm
60 | *.msp
61 |
62 | # Windows shortcuts
63 | *.lnk
64 |
65 | # General
66 | .DS_Store
67 | .AppleDouble
68 | .LSOverride
69 |
70 | # Files that might appear in the root of a volume
71 | .DocumentRevisions-V100
72 | .fseventsd
73 | .Spotlight-V100
74 | .TemporaryItems
75 | .Trashes
76 | .VolumeIcon.icns
77 | .com.apple.timemachine.donotpresent
78 |
79 | # Directories potentially created on remote AFP share
80 | .AppleDB
81 | .AppleDesktop
82 | Network Trash Folder
83 | Temporary Items
84 | .apdisk
85 |
86 | # Byte-compiled / optimized / DLL files
87 | __pycache__/
88 | *.py[cod]
89 | *$py.class
90 |
91 | # C extensions
92 | *.so
93 |
94 | # Environments
95 | .env
96 | .venv
97 | env/
98 | venv/
99 | ENV/
100 | env.bak/
101 | venv.bak/
102 |
103 | # Dropbox settings and caches
104 | .dropbox
105 | .dropbox.attr
106 | .dropbox.cache
107 |
108 | # Visual Studio Code dir
109 | .vscode
110 |
--------------------------------------------------------------------------------
/.gitpod.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gitpod/workspace-full-vnc
2 | RUN sudo apt-get update
3 | RUN sudo apt-get install -y libgtk-3-dev
4 | RUN sudo apt-get install -y libxcb-xinerama0
5 | RUN sudo apt-get install -y libxcb-render-util0
6 | RUN sudo apt-get install -y libxcb-randr0
7 | RUN sudo apt-get install -y libxcb-shape0
8 | RUN sudo apt-get install -y libxcb-icccm4
9 | RUN sudo apt-get install -y libxcb-image0
10 | RUN sudo apt-get install -y libxcb-keysyms1
11 | RUN sudo apt-get install -y libxkbcommon-x11-0
12 | # The libraries below are not needed for pytplot to work as is, but are listed as requirements for PyQT5
13 | # If things don't work, maybe try installing these
14 | #RUN sudo apt-get install -y libfontconfig1
15 | #RUN sudo apt-get install -y libfreetype6
16 | #RUN sudo apt-get install -y libxext6
17 | #RUN sudo apt-get install -y libxcb1
18 | #RUN sudo apt-get install -y libx11-6
19 | #RUN sudo apt-get install -y libice6
20 | #RUN sudo apt-get install -y libglib2.0-0
21 | #RUN sudo apt-get install -y libpthread-stubs0-dev
22 | #RUN sudo apt-get install -y libsm6
23 | #RUN sudo apt-get install -y libxcb-util-dev
24 | #RUN sudo apt-get install -y libxrender-dev
25 | #RUN sudo apt-get install -y libxcb-render0
26 | #RUN sudo apt-get install -y libxkbcommon0
27 | #RUN sudo apt-get install -y libxcb-sync1
28 | #RUN sudo apt-get install -y libxcb-xfixes0
29 | #RUN sudo apt-get install -y libxcb-shm0
30 | RUN sudo rm -rf /var/lib/apt/lists/*
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | # This configuration file was automatically generated by Gitpod.
2 | # Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file)
3 | # and commit this file to your remote git repository to share the goodness with others.
4 |
5 | image:
6 | file: .gitpod.Dockerfile
7 |
8 | tasks:
9 | - init: pip install -r requirements.txt
10 | - init: pip install pyspedas
11 |
12 |
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 MAVEN SDC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.rst
2 | include README.md
3 | include LICENSE
4 | include test_data.tplot
5 | include docs/*.html
6 | include docs/*.txt
7 | include pytplot/*.coffee
8 | include pytplot/sampledata/*.tplot
9 | include pytplot/sampledata/*.pytplot
10 | include pytplot/tplot_math
11 | recursive-include pytplot/HTMLPlotter *
12 | recursive-include pytplot/QtPlotter *
13 | include pytplot/version.txt
14 | exclude tplot2.py
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/MAVENSDC/PyTplot/actions)
2 | [](https://zenodo.org/badge/latestdoi/68843190)
3 |
4 |
5 | Full Documentation here: [https://pytplot.readthedocs.io/en/latest/](https://pytplot.readthedocs.io/en/latest/)
6 |
7 |
8 | Pytplot is a python package which aims to mimic the functionality of the IDL "tplot" libraries.
9 |
10 | These plots have several user interaction tools built in, such as zooming and panning. The can be exported as standalone HTML files (to retain their interactivity) or as static PNG files.
11 |
12 | Pytplot can be used in python scripts, or interactively through IPython and the Jupyter notebook.
13 |
14 |
15 | Quick Start
16 | ===========
17 |
18 | Install Python
19 | ---------------
20 |
21 | You will need the Anaconda distribution of Python 3 in order to run pytplot.
22 |
23 | `Anaconda `_ comes with a suite of packages that are useful for data science.
24 |
25 |
26 | Install pytplot
27 | ----------------
28 |
29 | Open up a terminal, and type::
30 |
31 | pip install pytplot
32 |
33 | This will install pytplot and all of it's dependencies.
34 |
35 | You will also need to install nodejs. This can be done through Anaconda with the following command::
36 |
37 | conda install -c bokeh nodejs
38 |
39 | Running Pytplot
40 | ---------------
41 |
42 | To start using pytplot in a similar manner to IDL tplot, start up an interactive environment through the terminal command::
43 |
44 | ipython
45 |
46 | or, if you prefer the jupyter interactive notebook::
47 |
48 | jupyter notebook
49 |
50 | then, just import the package by typing the command::
51 |
52 | import pytplot
53 |
54 | A demo/tutorial can be found here: `docs/pytplot_tutorial.html `_.
55 |
56 | A full description of each function can be found in `docs/build/index.html `_.
57 |
58 | Alternatively, the PDF version is located in `docs/build/PyTplot.pdf `_.
59 |
60 | Contact
61 | =============
62 |
63 | If you have any suggestions or notice any problems, don't hesitate to contact Bryan Harter: harter@lasp.colorado.edu
64 |
65 |
66 | Copyright 2019 Regents of the University of Colorado. All Rights Reserved.
67 | Released under the MIT license.
68 | This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
69 | Verify current version before use at: https://github.com/MAVENSDC/PyTplot
70 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 |
2 | ##########
3 | pytplot
4 | ##########
5 |
6 | Pytplot is a python package which aims to mimic the functionality of the IDL "tplot" libraries. The primary routine (tplot) generates HTML files for the specified plots, and automatically opens the files in a Qt interface.
7 |
8 | These plots have several user interaction tools built in, such as zooming and panning. The can be exported as standalone HTML files (to retain their interactivity) or as static PNG files.
9 |
10 | Pytplot can be used in python scripts, or interactively through IPython and the Jupyter notebook.
11 |
12 | How It Works
13 | =============
14 |
15 | Data is read into pytplot by using the "store_data" command. Each dataset is assigned a unique name by the user.
16 |
17 | The data is stored in a "tplot variable" class. The tplot variables contain all the information required to create a plot of the dataset. The details of the plot, such as axis titles, types, line colors, etc, can be changed through other functions in pytplot.
18 |
19 | When you are ready to create a graph of your dataset(s), supply the dataset names you wish to plot to the "tplot" function, and a graph will be generated.
20 |
21 |
22 |
23 | Install Python
24 | =============
25 |
26 | You will need the Anaconda distribution of Python 3 in order to run pytplot.
27 |
28 | `Anaconda `_ comes with a suite of packages that are useful for data science.
29 |
30 |
31 | Install pytplot
32 | =============
33 |
34 | Open up a terminal, and type::
35 |
36 | pip install pytplot
37 |
38 | This will install pytplot and all of it's dependencies.
39 |
40 | You will also need to install nodejs. This can be done through Anaconda with the following command::
41 |
42 | conda install -c bokeh nodejs
43 |
44 | Running Pytplot
45 | =============
46 |
47 | To start using pytplot in a similar manner to IDL tplot, start up an interactive environment through the terminal command::
48 |
49 | ipython
50 |
51 | or, if you prefer the jupyter interactive notebook::
52 |
53 | jupyter notebook
54 |
55 | then, just import the package by typing the command::
56 |
57 | import pytplot
58 |
59 | A demo/tutorial can be found here: `docs/pytplot_tutorial.html `_.
60 |
61 | A full description of each function can be found in `docs/build/index.html `_.
62 |
63 | Alternatively, the PDF version is located in `docs/build/PyTplot.pdf `_.
64 |
65 | Contact
66 | =============
67 |
68 | If you have any suggestions or notice any problems, don't hesitate to contact Bryan Harter: harter@lasp.colorado.edu
69 |
70 |
71 | # Copyright 2017 Regents of the University of Colorado. All Rights Reserved.
72 | # Released under the MIT license.
73 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
74 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SOURCEDIR = source
8 | BUILDDIR = build
9 |
10 | # Put it first so that "make" without argument is like "make help".
11 | help:
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13 |
14 | .PHONY: help Makefile
15 |
16 | # Catch-all target: route all unknown targets to Sphinx using the new
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 | %: Makefile
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/source/_images/2d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/docs/source/_images/2d.png
--------------------------------------------------------------------------------
/docs/source/_images/3d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/docs/source/_images/3d.png
--------------------------------------------------------------------------------
/docs/source/_images/extra_x_axes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/docs/source/_images/extra_x_axes.png
--------------------------------------------------------------------------------
/docs/source/_images/gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/docs/source/_images/gui.png
--------------------------------------------------------------------------------
/docs/source/_images/interactivity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/docs/source/_images/interactivity.png
--------------------------------------------------------------------------------
/docs/source/_images/sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/docs/source/_images/sample.png
--------------------------------------------------------------------------------
/docs/source/_images/spec_slicer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/docs/source/_images/spec_slicer.png
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # http://www.sphinx-doc.org/en/master/config
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 | sys.path.insert(0, os.path.abspath('../..'))
16 |
17 |
18 | # -- Project information -----------------------------------------------------
19 |
20 | project = 'pytplot'
21 | copyright = '2019, Bryan Harter'
22 | author = 'Bryan Harter'
23 |
24 |
25 | # -- General configuration ---------------------------------------------------
26 |
27 | # Add any Sphinx extension module names here, as strings. They can be
28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
29 | # ones.
30 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon']
31 |
32 | # Add any paths that contain templates here, relative to this directory.
33 | templates_path = ['_templates']
34 |
35 | # List of patterns, relative to source directory, that match files and
36 | # directories to ignore when looking for source files.
37 | # This pattern also affects html_static_path and html_extra_path.
38 | exclude_patterns = []
39 |
40 | # The suffix of source filenames.
41 | source_suffix = '.rst'
42 |
43 | # The master toctree document.
44 | master_doc = 'index'
45 |
46 | # -- Options for HTML output -------------------------------------------------
47 |
48 | # The theme to use for HTML and HTML Help pages. See the documentation for
49 | # a list of builtin themes.
50 | #
51 | html_theme = 'sphinx_rtd_theme'
52 |
53 | # Add any paths that contain custom static files (such as style sheets) here,
54 | # relative to this directory. They are copied after the builtin static files,
55 | # so a file named "default.css" will overwrite the builtin "default.css".
56 | html_static_path = ['_static']
57 |
58 |
59 | autodoc_mock_imports = ['sip', 'PyQt5', 'PyQt5.QtGui', 'PyQt5.QtCore', 'PyQt5.QtWidgets']
--------------------------------------------------------------------------------
/docs/source/helper_functions.rst:
--------------------------------------------------------------------------------
1 | Helper Functions
2 | =================
3 |
4 | These are functions that help get or set attributes of the data. All of these were basically mapped one-to-one from IDL routines.
5 |
6 | .. autofunction:: pytplot.del_data
7 | .. autofunction:: pytplot.get_data
8 | .. autofunction:: pytplot.tplot_copy
9 | .. autofunction:: pytplot.replace_data
10 | .. autofunction:: pytplot.get_timespan
11 | .. autofunction:: pytplot.get_ylimits
12 | .. autofunction:: pytplot.timebar
13 | .. autofunction:: pytplot.timespan
14 | .. autofunction:: pytplot.timestamp
15 | .. autofunction:: pytplot.tplot_names
16 | .. autofunction:: pytplot.tplot_rename
17 | .. autofunction:: pytplot.xlim
18 | .. autofunction:: pytplot.ylim
19 | .. autofunction:: pytplot.zlim
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. pytplot documentation master file, created by
2 | sphinx-quickstart on Tue Jun 18 11:12:58 2019.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | PyTplot's Documentation
7 | =======================
8 |
9 |
10 | .. toctree::
11 | :maxdepth: 2
12 |
13 | introduction
14 | tplot_variables
15 | reading_in_data
16 | options
17 | tplot_options
18 | helper_functions
19 | plotting
20 | linking
21 | interactivity
22 | math_routines
23 |
--------------------------------------------------------------------------------
/docs/source/linking.rst:
--------------------------------------------------------------------------------
1 | Linking Two Variables and Non-Time Series Plots
2 | ==================================================
3 |
4 | Sometimes plots other than time series may be desired. Typically, this is either altitude or latitude/longitude plots.
5 |
6 | Because tplot variables always have time on their x-axis, in order to get these types of plots you need to tell pytplot that two variables
7 | are related to one another.
8 |
9 | In other words, if you have a tplot variable for altitude named "spacecraft_alt" and while the data is stored in the variable "spacecraft_data", you can tell pytplot that the two are related with the following command::
10 |
11 | pytplot.link("spacecraft_data", "spacecraft_alt", link_type = 'alt')
12 |
13 | If the data are not on the same time cadence, the linked variable will be interpolated to match that of the data variable at the time of plotting.
14 |
15 |
16 | Why Linking?
17 | --------------
18 | One may ask, why not just allow altitude/latitude/longitude to be the xaxis of the tplot variables?
19 | The answer, aside from the fact that this is a time-series manipulation library, is that time is still retained in the "backend" of the plots.
20 | For instance, the time value will display when you hover over the data,
21 | and if you mark specific times with either a vertical Time Bar or Region of Interest, then that point/area will be highlighted on the plot.
22 |
23 |
24 | Altitude Plot Example
25 | ---------------------
26 |
27 | The following uses data taken from the MAVEN mission::
28 |
29 | # Store Data from a dictionary variable named "insitu" (read into python from elsewhere)
30 | pytplot.store_data('sc_alt', data={'x':insitu['Time'] , 'y':insitu['SPACECRAFT']['ALTITUDE']})
31 | pytplot.store_data('euv_low', data={'x':insitu['Time'] , 'y':insitu['EUV']['IRRADIANCE_LOW']})
32 | pytplot.store_data('euv_lyman', data={'x':insitu['Time'] , 'y':insitu['EUV']['IRRADIANCE_LYMAN']})
33 | pytplot.store_data('euv_mid', data={'x':insitu['Time'] , 'y':insitu['EUV']['IRRADIANCE_MID']})
34 |
35 | # Link the EUV variables to "sc_alt"
36 | pytplot.link(["euv_mid"], "sc_alt", link_type='alt')
37 | pytplot.link(["euv_low"], "sc_alt", link_type='alt')
38 | pytplot.link(["euv_lyman"], "sc_alt", link_type='alt')
39 | #Specify that you'd like to overplot
40 | pytplot.store_data('euv', data=["euv_low","euv_mid","euv_lyman"])
41 |
42 | #Set Plot options
43 | pytplot.options("euv", 'alt', 1)
44 | pytplot.options("euv", 'ylog', 1)
45 | pytplot.ylim("euv", .000001, .02)
46 | pytplot.options("euv", "legend_names", ["Low", "Mid", "Lyman"])
47 |
48 | #Add a big blue marker at 3:23:00
49 | pytplot.timebar('2015-12-25 03:23:00', varname='euv', color='blue', thick=10)
50 |
51 | #Plot!
52 | pytplot.tplot("euv")
53 |
54 |
55 |
56 |
57 | .. raw:: html
58 | :file: _images/altitude.html
59 |
60 |
61 |
62 |
63 |
64 | Map Plot Example
65 | ----------------
66 |
67 | The following is the same example as above, but this time plotted vs latitude and longitude over the surface of Mars.
68 | It also plots only the euv_lyman variable::
69 |
70 | # Store Data from a dictionary variable named "insitu" (read into python from elsewhere)
71 | pytplot.store_data('sc_lon', data={'x':insitu['Time'] , 'y':insitu['SPACECRAFT']['SUB_SC_LONGITUDE']})
72 | pytplot.store_data('sc_lat', data={'x':insitu['Time'] , 'y':insitu['SPACECRAFT']['SUB_SC_LATITUDE']})
73 | pytplot.store_data('euv_lyman', data={'x':insitu['Time'] , 'y':insitu['EUV']['IRRADIANCE_LYMAN']})
74 |
75 | # Link latitude and longitude
76 | pytplot.link(["euv_lyman"], "sc_lat", link_type='lat')
77 | pytplot.link(["euv_lyman"], "sc_lon", link_type='lon')
78 |
79 | # Set Plot options
80 | pytplot.options("euv_lyman", 'map', 1)
81 | pytplot.options("euv_lyman", 'zlog', 1)
82 | pytplot.options("euv_lyman", 'basemap', 'C:/maps/MOLA_BW_2500x1250.jpg'))
83 |
84 | #Add a big blue marker at 3:23:00
85 | pytplot.timebar('2015-12-25 03:23:00', varname='euv_lyman', color='blue', thick=20)
86 |
87 | # Plot!
88 | pytplot.tplot("euv_lyman")
89 |
90 | .. raw:: html
91 | :file: _images/map.html
92 |
93 |
94 | If pyqtgraph is used to plot the map plot instead, a little marker will appear on the map at the time the user is hovering over with their mouse.
--------------------------------------------------------------------------------
/docs/source/math_routines.rst:
--------------------------------------------------------------------------------
1 | Math Routines
2 | ======================
3 |
4 | Arithmetic
5 | ----------
6 | .. autofunction:: pytplot.tplot_math.add
7 | .. autofunction:: pytplot.tplot_math.subtract
8 | .. autofunction:: pytplot.tplot_math.multiply
9 | .. autofunction:: pytplot.tplot_math.divide
10 | .. autofunction:: pytplot.tplot_math.crop
11 | .. autofunction:: pytplot.tplot_math.tinterp
12 |
13 | Add Across Columns
14 | ------------------
15 | .. autofunction:: pytplot.tplot_math.add_across
16 |
17 | Average over time
18 | -----------------
19 | .. autofunction:: pytplot.tplot_math.avg_res_data
20 |
21 | Clip Data
22 | ---------
23 | .. autofunction:: pytplot.tplot_math.clip
24 |
25 | Deflag Data
26 | -----------
27 | .. autofunction:: pytplot.tplot_math.deflag
28 |
29 | Degap Data
30 | ----------
31 | .. autofunction:: pytplot.tplot_math.degap
32 |
33 | Derivative
34 | ----------
35 | .. autofunction:: pytplot.tplot_math.derive
36 |
37 | Flatten Data
38 | ------------
39 | .. autofunction:: pytplot.tplot_math.flatten
40 |
41 | Interpolate through NaN values
42 | ------------------------------
43 | .. autofunction:: pytplot.tplot_math.interp_nan
44 |
45 | Join/Split Data
46 | ---------------
47 | .. autofunction:: pytplot.tplot_math.join_vec
48 | .. autofunction:: pytplot.tplot_math.split_vec
49 |
50 | Power Spectrum
51 | --------------
52 | .. autofunction:: pytplot.tplot_math.pwr_spec
53 |
54 | Resample Data
55 | -------------
56 | .. autofunction:: pytplot.tplot_math.resample
57 |
58 | Spectrum Multiplication
59 | -----------------------
60 | .. autofunction:: pytplot.tplot_math.spec_mult
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/docs/source/options.rst:
--------------------------------------------------------------------------------
1 | Options
2 | =============
3 |
4 | .. autofunction:: pytplot.options
--------------------------------------------------------------------------------
/docs/source/plotting.rst:
--------------------------------------------------------------------------------
1 | Plotting
2 | =============
3 |
4 | tplot function
5 | --------------
6 |
7 |
8 | All plotting is called through the "tplot()" function. If you'd like to customize how you plots are displayed,
9 | it is assume that you have set them up prior to calling this function.
10 |
11 | .. autofunction:: pytplot.tplot
12 |
13 |
14 | Oveplotting
15 | -----------
16 |
17 | To combine two or more variables in the same plot, you need to create a new variable like so::
18 |
19 | pytplot.store_data("new_variable", data=["variable1_to_overplot", "variable2_to_overplot"])
20 |
21 | Then when you plot this new variable, it will be a combination of the two variables given in "data".
22 |
23 | .. note::
24 | Each variable should still retain the plot options you set for it, but I am still working out the kinks.
25 |
26 |
27 | Extra X axes
28 | -------------
29 |
30 | A commonly used feature of tplot is adding extra x axes (in addition to time), on the bottom of the plot.
31 |
32 | To do so in pytplot, specify which tplot variable(s) you'd like to to be included on the axis by passing them to the "var_label" option in tplot::
33 |
34 | pytplot.tplot("variable1", var_label = ["variable2", "variable3"])
35 |
36 | .. note::
37 | Unfortunately, in the Bokeh plots currently the extra x axes must be linearly increasing in order to display properly. Hopefully we can determine a way to map variables onto the axes at some point in the future.
38 |
39 |
40 | A common use case would be orbit numbers or spacecraft position. Here is an example of multiple x axes below:
41 |
42 | .. image:: _images/extra_x_axes.png
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/docs/source/reading_in_data.rst:
--------------------------------------------------------------------------------
1 | Reading in Data
2 | =============================
3 |
4 | Manual Input
5 | ------------
6 | .. autofunction:: pytplot.store_data
7 |
8 | CDF Reader
9 | ----------
10 | .. autofunction:: pytplot.cdf_to_tplot
11 |
12 | NetCDF Reader
13 | -------------
14 | .. autofunction:: pytplot.netcdf_to_tplot
15 |
16 | IDL Restore
17 | -----------
18 | .. autofunction:: pytplot.tplot_restore
19 |
--------------------------------------------------------------------------------
/docs/source/tplot_options.rst:
--------------------------------------------------------------------------------
1 | Tplot Options
2 | =====================
3 |
4 | .. autofunction:: pytplot.tplot_options
--------------------------------------------------------------------------------
/docs/source/tplot_variables.rst:
--------------------------------------------------------------------------------
1 | Tplot Variables
2 | ================
3 |
4 | Every piece of data read into tplot is stored into global "tplot variable" (sometimes called "tvar" in the documentation).
5 |
6 | Every routine that deals with these variables uses simply their names to reference them, you do not need to pass the actual variables themselves around from function to function.
7 |
8 | If you would like to access the variable directly, they are stored in a global OrderedDict object called "data_quants", and can be found like so::
9 |
10 | pytplot.data_quants['tplot_variable_name_here']
11 |
12 | Tplot variables are kept global because it is not unheard of for >50 variables to be read in from a single file satellite data file, and naming/keeping track of each variable can become cumbersome.
13 |
14 |
15 | Internal Structure
16 | ------------------
17 |
18 | Users do not necessarily need to know the internal details of the tplot variables to use the library, but if you'd like to use tplot variables in other libraries this is good information to know.
19 |
20 | "Tplot variable" is really just a fancy name for an Xarray DataArrray object. Tplot variables are just a type of DataArray that have standardized attributes so that PyTplot knows how to work with them
21 |
22 | * There is always a "time" coordinate, and that time is in seconds since 1970.
23 | * For spectogram data, there is always a "spec_bins" coordinate that keeps track of the bin sizes on the yaxis
24 | * There is a "plot_options" attribute given to each tplot variable that is a large nested dictionary that keeps track of all plotting objects for the variable.
25 | * The dimensions stored are always "time", "y", "v", and for more mutlidimensional data there is "v2", "v3", etc
26 |
27 | You can also access the pure underlying numpy arrays like so::
28 |
29 | #Data
30 | pytplot.data_quants['variable_name'].values
31 | #Time
32 | pytplot.data_quants['variable_name'].coords['time'].values
33 | #Spec bins
34 | pytplot.data_quants['variable_name'].coords['spec_bins'].values
35 |
36 |
--------------------------------------------------------------------------------
/docs/test_data.tplot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/docs/test_data.tplot
--------------------------------------------------------------------------------
/meta.yaml:
--------------------------------------------------------------------------------
1 | {% set name = "pytplot" %}
2 | {% set version = "1.7.28" %}
3 |
4 | package:
5 | name: "{{ name|lower }}"
6 | version: "{{ version }}"
7 |
8 | source:
9 | git_url: https://github.com/MAVENSDC/pytplot.git
10 | git_rev: v1.7.28
11 |
12 | build:
13 | number: 0
14 | script: "{{ PYTHON }} -m pip install . -vv"
15 |
16 | requirements:
17 | build:
18 | - setuptools
19 | - python
20 | - pip
21 | run:
22 | - python
23 | - numpy
24 | - bokeh
25 | - pandas
26 | - matplotlib
27 | - scipy
28 | - xarray
29 | - pyqtgraph
30 | - cdflib
31 |
32 | about:
33 | home: "https://github.com/MAVENSDC/pytplot"
34 | license: "MIT"
35 | summary: "Pytplot is an effort to replicate the functionality IDL tplot library in python"
36 | doc_url: "https://pytplot.readthedocs.io"
37 | dev_url: "https://github.com/MAVENSDC/pyptlot"
38 |
39 | extra:
40 | recipe-maintainers:
41 | - MAVENSDC
42 |
--------------------------------------------------------------------------------
/pyqtgraphtestertemp.py:
--------------------------------------------------------------------------------
1 | import pyspedas
2 | pyspedas.mms.fgm()
3 |
4 | from pytplot import tplot
5 | tplot(['mms1_fgm_b_gse_srvy_l2_btot', 'mms1_fgm_b_gse_srvy_l2_bvec'])
--------------------------------------------------------------------------------
/pytplot/AncillaryPlots/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/AncillaryPlots/__init__.py
--------------------------------------------------------------------------------
/pytplot/AncillaryPlots/position_mars_2d.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 | import pytplot
3 |
4 |
5 | def position_mars_2d(temp=None):
6 | '''
7 | This is a simple plot that shows spacecraft position relative to Mars in MSO coordinates
8 | '''
9 |
10 | # Set Mars Diameter constant
11 | MARS_DIAMETER = 3389.5 * 2
12 |
13 | # Set up the 2D interactive plot
14 | window = pytplot.tplot_utilities.get_available_qt_window(name='2D_MARS')
15 | window.newlayout(pg.GraphicsWindow())
16 | window.resize(900, 300)
17 | window.setWindowTitle('Mars Position 2D Window')
18 |
19 | # Add the 3 plots for 3 different views
20 | plot1 = window.centralWidget().addPlot(title='Position MSO x-y', row=0, col=0)
21 | plot1.setLabel('left', text = 'MSO Y', units = 'km')
22 | plot1.setLabel('bottom', text='MSO X', units='km')
23 | plot2 = window.centralWidget().addPlot(title='Position MSO y-z', row=0, col=1)
24 | plot2.setLabel('left', text='MSO Z', units='km')
25 | plot2.setLabel('bottom', text='MSO Y', units='km')
26 | plot3 = window.centralWidget().addPlot(title='Position MSO x-z', row=0, col=2)
27 | plot3.setLabel('left', text='MSO Z', units='km')
28 | plot3.setLabel('bottom', text='MSO X', units='km')
29 |
30 |
31 |
32 | # Make it so that whenever this first starts up, you just have an empty plot
33 | plot_data1 = plot1.plot([], [], symbol='o', symbol_size=14)
34 | plot_data2 = plot2.plot([], [], symbol='o', symbol_size=14)
35 | plot_data3 = plot3.plot([], [], symbol='o', symbol_size=14)
36 |
37 | # Add the representation of mars on the first plot
38 | mars1 = pg.QtGui.QGraphicsEllipseItem(-1*MARS_DIAMETER/2,-1*MARS_DIAMETER/2,MARS_DIAMETER,MARS_DIAMETER)
39 | mars1.setPen(pg.mkPen(None))
40 | mars1.setBrush(pg.mkBrush('r'))
41 | mars1.setZValue(-1)
42 | mars1.setSpanAngle(180 * 16)
43 | mars1.setStartAngle(-90 * 16)
44 | mars1_dark = pg.QtGui.QGraphicsEllipseItem(-1 * MARS_DIAMETER / 2, -1 * MARS_DIAMETER / 2, MARS_DIAMETER, MARS_DIAMETER)
45 | mars1_dark.setPen(pg.mkPen(None))
46 | mars1_dark.setBrush(pg.mkBrush(.2))
47 | mars1_dark.setZValue(-1)
48 | mars1_dark.setSpanAngle(180 * 16)
49 | mars1_dark.setStartAngle(90 * 16)
50 | plot1.vb.addItem(mars1)
51 | plot1.vb.addItem(mars1_dark)
52 | plot1.setXRange(-10000, 10000)
53 | plot1.setYRange(-10000, 10000)
54 |
55 | # Add the representation of mars on the second plot
56 | mars2 = pg.QtGui.QGraphicsEllipseItem(-1 *MARS_DIAMETER / 2, -1 * MARS_DIAMETER / 2, MARS_DIAMETER, MARS_DIAMETER)
57 | mars2.setPen(pg.mkPen(None))
58 | mars2.setBrush(pg.mkBrush('r'))
59 | mars2.setZValue(-1)
60 | plot2.vb.addItem(mars2)
61 | plot2.setXRange(-10000, 10000)
62 | plot2.setYRange(-10000, 10000)
63 |
64 | # Add the representation of mars on the third plot
65 | mars3 = pg.QtGui.QGraphicsEllipseItem(-1 * MARS_DIAMETER / 2, -1 * MARS_DIAMETER / 2, MARS_DIAMETER, MARS_DIAMETER)
66 | mars3.setPen(pg.mkPen(None))
67 | mars3.setBrush(pg.mkBrush('r'))
68 | mars3.setZValue(-1)
69 | mars3.setSpanAngle(180 * 16)
70 | mars3.setStartAngle(-90 * 16)
71 | mars3_dark = pg.QtGui.QGraphicsEllipseItem(-1 * MARS_DIAMETER / 2, -1 * MARS_DIAMETER / 2, MARS_DIAMETER, MARS_DIAMETER)
72 | mars3_dark.setPen(pg.mkPen(None))
73 | mars3_dark.setBrush(pg.mkBrush(.2))
74 | mars3_dark.setZValue(-1)
75 | mars3_dark.setSpanAngle(180 * 16)
76 | mars3_dark.setStartAngle(90 * 16)
77 | plot3.vb.addItem(mars3)
78 | plot3.vb.addItem(mars3_dark)
79 | plot3.setXRange(-10000, 10000)
80 | plot3.setYRange(-10000, 10000)
81 |
82 | # Define the update function on mouse moved events
83 | def update(t, name):
84 | # Get the xarray for x/y/z positions of the spacecraft
85 | if 'x' in pytplot.data_quants[name].attrs['plot_options']['links']:
86 | x_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['x']]
87 | y_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['y']]
88 | z_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['z']]
89 | elif 'mso_x' in pytplot.data_quants[name].attrs['plot_options']['links']:
90 | x_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['mso_x']]
91 | y_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['mso_y']]
92 | z_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['mso_z']]
93 | else:
94 | return
95 |
96 | # Use nearest neighbor to find closest x/y/z values
97 | new_x = x_tvar.sel(time=t, method='nearest').values
98 | new_y = y_tvar.sel(time=t, method='nearest').values
99 | new_z = z_tvar.sel(time=t, method='nearest').values
100 |
101 | # This moves the Mars spheres either to the front or the back depending on the location of the spacecraft
102 | if new_z < 0:
103 | mars1.setZValue(1)
104 | mars1_dark.setZValue(1)
105 | else:
106 | mars1.setZValue(-1)
107 | mars1_dark.setZValue(-1)
108 | if new_x < 0:
109 | mars2.setZValue(1)
110 | else:
111 | mars2.setZValue(-1)
112 | if new_y < 0:
113 | mars3.setZValue(1)
114 | mars3_dark.setZValue(1)
115 | else:
116 | mars3.setZValue(-1)
117 | mars3_dark.setZValue(-1)
118 |
119 | # Sets the position of the spacecraft in each window
120 | plot_data1.setData([new_x], [new_y])
121 | plot_data2.setData([new_y], [new_z])
122 | plot_data3.setData([new_x], [new_z])
123 |
124 | # Register the update function to pytplot
125 | pytplot.hover_time.register_listener(update)
126 |
127 | # Turn on the window!
128 | window.show()
129 |
130 |
--------------------------------------------------------------------------------
/pytplot/AncillaryPlots/position_mars_3d.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 |
3 | def position_mars_3d(temp=None):
4 | '''
5 | This is a simple 3D window that shows a spacecraft in MSO coordinates.
6 | This tool will look for links to the tplot variable that are named either "x/y/z" or "mso_(x/y/z)"
7 | '''
8 |
9 | # Import 3D functionality from opengl
10 | try:
11 | import pyqtgraph.opengl as gl
12 | from pyqtgraph.Qt import QtGui
13 | from OpenGL.GL import glLightModelfv, glLightfv, GL_LIGHT0, GL_POSITION, \
14 | GL_LIGHT_MODEL_AMBIENT, GL_LIGHTING, glEnable, GL_COLOR_MATERIAL, \
15 | GL_AMBIENT, GL_SPECULAR, GL_DIFFUSE, glMaterial, GL_FRONT_AND_BACK, \
16 | GL_AMBIENT_AND_DIFFUSE, GL_FRONT, glColorMaterial, GL_PROJECTION, \
17 | glMatrixMode, glLoadIdentity, glTexParameterf
18 | except:
19 | raise("In order to use the 3D position viewing tool you must pip install PyOpenGL")
20 |
21 | # Tell Pytplot about new window
22 | window = pytplot.tplot_utilities.get_available_qt_window(name='3D_MARS')
23 | window.resize(1000, 600)
24 | window.setWindowTitle('3D Mars Window')
25 |
26 | # Defining a new class that keeps track of spacecraft position and moves the
27 | class PlanetView(gl.GLViewWidget):
28 | spacecraft_x = 0
29 | spacecraft_y = 0
30 | spacecraft_z = 0
31 | tvar_name = 'temp' # Store the name of the tplot variable stored, so we know if we need to redraw the orbit
32 | def paintGL(self, region=None, viewport=None, useItemNames=False):
33 | glLightfv(GL_LIGHT0, GL_POSITION, [-100000,0,0,0])
34 | super().paintGL(region=region, viewport=viewport, useItemNames=useItemNames)
35 |
36 | plot1 = PlanetView()
37 |
38 | # Set up the "sun"
39 | glLightModelfv(GL_LIGHT_MODEL_AMBIENT, [.3,.3,.3,1.0])
40 | light_position = [-100000,0,0,0]
41 | glLightfv(GL_LIGHT0, GL_POSITION, light_position)
42 | glEnable(GL_LIGHTING)
43 | glEnable(GL_LIGHT0)
44 | glEnable(GL_COLOR_MATERIAL)
45 | glLightfv(GL_LIGHT0, GL_AMBIENT, [1,1,1,0])
46 | glLightfv(GL_LIGHT0, GL_DIFFUSE, [1,1,1,1])
47 | glLightfv(GL_LIGHT0, GL_SPECULAR, [1,0,0,0])
48 |
49 | # Create Mars and spacecraft icons (assuming spherical spacecraft)
50 | md = gl.MeshData.sphere(rows=100, cols=220)
51 | mars = gl.GLMeshItem(meshdata=md, smooth=True, color=(.5, 0, 0, 1), glOptions='opaque')
52 | mars.translate(0, 0, 0)
53 | mars.scale(3390, 3390, 3390)
54 | spacecraft = gl.GLMeshItem(meshdata=md, smooth=True, color=(1, 1, 1, 1))
55 | spacecraft.translate(plot1.spacecraft_x, plot1.spacecraft_y, plot1.spacecraft_z)
56 |
57 | spacecraft.scale(200, 200, 200)
58 | orbit_path = gl.GLLinePlotItem()
59 | plot1.addItem(mars)
60 | plot1.addItem(spacecraft)
61 | plot1.addItem(orbit_path)
62 | glMaterial(GL_FRONT_AND_BACK, GL_SPECULAR, [.5,.5,.5,1])
63 | glEnable(GL_COLOR_MATERIAL)
64 | glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)
65 |
66 | # Put the planetview plot into the pytplot window
67 | window.setCentralWidget(plot1)
68 |
69 | # Move around the camera
70 | glMatrixMode(GL_PROJECTION)
71 | glLoadIdentity()
72 | plot1.setModelview()
73 | plot1.setCameraPosition(distance=30000, azimuth=0, elevation=0)
74 |
75 | # Turn on the window!
76 | window.show()
77 |
78 | # Define the function that is called on new hover_time updates
79 | def update(t,name):
80 | # Move spacecraft back to 0,0,0
81 | previous_x = plot1.spacecraft_x
82 | previous_y = plot1.spacecraft_y
83 | previous_z = plot1.spacecraft_z
84 | previous_tvar = plot1.tvar_name
85 |
86 | spacecraft.translate(-1*previous_x, -1*previous_y, -1*previous_z)
87 |
88 | # Get the xarray for x/y/z positions of the spacecraft
89 | if 'x' in pytplot.data_quants[name].attrs['plot_options']['links']:
90 | x_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['x']]
91 | y_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['y']]
92 | z_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['z']]
93 | elif 'mso_x' in pytplot.data_quants[name].attrs['plot_options']['links']:
94 | x_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['mso_x']]
95 | y_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['mso_y']]
96 | z_tvar = pytplot.data_quants[pytplot.data_quants[name].attrs['plot_options']['links']['mso_z']]
97 | else:
98 | return
99 |
100 | if name != previous_tvar:
101 | import numpy as np
102 | pathasdf = np.array((x_tvar.data, y_tvar.data, z_tvar.data), dtype=float).T
103 | orbit_path.setData(pos = pathasdf)
104 |
105 |
106 | # Get the nearest x/y/z of the hover time
107 | new_x = x_tvar.sel(time=t, method='nearest').values
108 | new_y = y_tvar.sel(time=t, method='nearest').values
109 | new_z = z_tvar.sel(time=t, method='nearest').values
110 |
111 | # Move the spacecraft
112 | spacecraft.translate(new_x,new_y,new_z)
113 | plot1.spacecraft_x, plot1.spacecraft_y, plot1.spacecraft_z = new_x, new_y, new_z
114 | plot1.tvar_name = name
115 | plot1.paintGL()
116 |
117 | # Register the above update function to the called functions
118 | pytplot.hover_time.register_listener(update)
119 |
120 | return
121 |
122 |
--------------------------------------------------------------------------------
/pytplot/HTMLPlotter/CustomModels/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/HTMLPlotter/CustomModels/__init__.py
--------------------------------------------------------------------------------
/pytplot/HTMLPlotter/CustomModels/colorbarsidetitle.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from bokeh.models.annotations import ColorBar
7 | from bokeh.util.compiler import TypeScript
8 |
9 | ts_code = '''
10 | import {ColorBar, ColorBarView} from "models/annotations/color_bar"
11 | import * as p from "core/properties"
12 | import {Context2d} from "core/util/canvas"
13 |
14 | const SHORT_DIM = 25
15 |
16 | export class ColorBarSideTitleView extends ColorBarView {
17 | model: ColorBarSideTitle
18 | protected image: HTMLCanvasElement
19 |
20 | compute_legend_dimensions(): {width: number, height: number} {
21 | const image_dimensions = this._computed_image_dimensions()
22 | const [image_height, image_width] = [image_dimensions.height, image_dimensions.width]
23 |
24 | const label_extent = this._get_label_extent()
25 | const title_extent = this._title_extent()
26 | const tick_extent = this._tick_extent()
27 | const {padding} = this.model
28 |
29 | let legend_height: number, legend_width: number
30 | legend_height = image_height
31 | legend_width = image_width + tick_extent + label_extent + title_extent + 2*padding
32 |
33 | return {width: legend_width, height: legend_height}
34 | }
35 |
36 | protected _draw_image(ctx: Context2d): void {
37 | const image = this._computed_image_dimensions()
38 | ctx.save()
39 | ctx.setImageSmoothingEnabled(false)
40 | ctx.globalAlpha = this.model.scale_alpha
41 | ctx.drawImage(this.image, 0, 0, image.width, image.height)
42 | if (this.visuals.bar_line.doit) {
43 | this.visuals.bar_line.set_value(ctx)
44 | ctx.strokeRect(0, 0, image.width, image.height)
45 | }
46 | ctx.restore()
47 | }
48 |
49 | protected _draw_title(ctx: Context2d): void {
50 | if (!this.visuals.title_text.doit)
51 | return
52 | const label_extent = this._get_label_extent()
53 | const tick_extent = this._tick_extent()
54 | const {padding} = this.model
55 | ctx.save()
56 | this.visuals.title_text.set_value(ctx)
57 | ctx.rotate(-Math.PI/2)
58 | ctx.fillText(this.model.title, -this.plot_view.frame._height.value * 0.5 , this.image.width + tick_extent + label_extent + 4*padding)
59 | ctx.textAlign="center"
60 | ctx.restore()
61 | }
62 |
63 | /*protected*/ _get_image_offset(): {x: number, y: number} {
64 | // Returns image offset relative to legend bounding box
65 | const x = this.model.padding
66 | const y = 0
67 | return {x, y}
68 | }
69 |
70 | _computed_image_dimensions(): {height: number, width: number} {
71 | const frame_height = this.plot_view.frame._height.value
72 | let height: number, width: number
73 | if (this.model.height == 'auto') {
74 | height = frame_height
75 | } else
76 | height = this.model.height
77 |
78 | width = this.model.width == 'auto' ? SHORT_DIM : this.model.width
79 |
80 | return {width, height}
81 | }
82 |
83 | // }}}
84 | }
85 |
86 | export namespace ColorBarSideTitle {
87 | export type Attrs = p.AttrsOf
88 |
89 | export type Props = ColorBar.Props
90 | }
91 |
92 | export interface ColorBarSideTitle extends ColorBarSideTitle.Attrs {}
93 |
94 | export class ColorBarSideTitle extends ColorBar {
95 | properties: ColorBarSideTitle.Props
96 |
97 | constructor(attrs?: Partial) {
98 | super(attrs)
99 | }
100 |
101 | static initClass(): void {
102 | this.prototype.default_view = ColorBarSideTitleView
103 | }
104 | }
105 |
106 | ColorBarSideTitle.initClass()
107 |
108 | '''
109 |
110 |
111 | class ColorBarSideTitle(ColorBar):
112 | __implementation__ = TypeScript(ts_code)
--------------------------------------------------------------------------------
/pytplot/HTMLPlotter/CustomModels/timestamp.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from bokeh.core.properties import String
7 | from bokeh.models import LayoutDOM
8 | from bokeh.util.compiler import TypeScript
9 |
10 | JS_CODE = '''
11 | import * as p from "core/properties"
12 | import {div, empty} from "core/dom"
13 | import {LayoutDOM, LayoutDOMView} from "models/layouts/layout_dom"
14 | import {LayoutItem} from "core/layout"
15 |
16 | export class TimeStampView extends LayoutDOMView {
17 | model: TimeStamp
18 |
19 | initialize(): void {
20 | super.initialize()
21 | }
22 | render(): void {
23 | empty(this.el)
24 | this.el.appendChild(div({
25 | style: {
26 | 'width': '800px',
27 | 'word-spacing': '10px',
28 | 'font-size': '11px',
29 | 'color': '#000000',
30 | 'background-color': '#ffffff',
31 | },
32 | }, `${this.model.text}`))
33 | }
34 |
35 | _update_layout(): void {
36 | this.layout = new LayoutItem()
37 | this.layout.set_sizing(this.box_sizing())
38 | }
39 |
40 | get child_models(): LayoutDOM[] {
41 | return []
42 | }
43 | }
44 |
45 |
46 | export namespace TimeStamp {
47 | export type Attrs = p.AttrsOf
48 |
49 | export type Props = LayoutDOM.Props & {
50 | text: p.Property
51 | }
52 | }
53 |
54 | export interface TimeStamp extends TimeStamp.Attrs {}
55 |
56 | export class TimeStamp extends LayoutDOM {
57 | properties: TimeStamp.Props
58 | static initClass(): void {
59 |
60 | this.prototype.default_view = TimeStampView
61 |
62 | this.define({
63 | text: [ p.String ]
64 | })
65 | }
66 | }
67 |
68 | TimeStamp.initClass()
69 |
70 | '''
71 |
72 |
73 | class TimeStamp(LayoutDOM):
74 | __implementation__ = TypeScript(JS_CODE)
75 | text = String(default = "Testing")
76 |
77 |
--------------------------------------------------------------------------------
/pytplot/HTMLPlotter/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from .TVarFigure1D import TVarFigure1D
7 | from .TVarFigureMap import TVarFigureMap
8 | from .TVarFigureAlt import TVarFigureAlt
9 | from .TVarFigureSpec import TVarFigureSpec
10 | from .generate import generate_stack
--------------------------------------------------------------------------------
/pytplot/HTMLPlotter/generate.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from __future__ import division
7 | import pytplot
8 | from bokeh.models import LinearAxis, Range1d
9 | from .CustomModels.timestamp import TimeStamp
10 | from bokeh.layouts import gridplot
11 | from bokeh.io import doc
12 | import numpy as np
13 |
14 |
15 | def generate_stack(name,
16 | var_label=None,
17 | auto_color=True,
18 | combine_axes=True,
19 | slice=True,
20 | vert_spacing=25):
21 |
22 | doc.curdoc().clear()
23 | num_plots = len(name)
24 |
25 | # Name for .html file containing plots
26 | out_name = ""
27 |
28 | if isinstance(var_label, int):
29 | var_label = list(pytplot.data_quants.keys())[var_label]
30 |
31 | # Vertical Box layout to store plots
32 | all_plots = []
33 | axis_types = []
34 | i = 0
35 |
36 | # Configure plot sizes
37 | total_psize = 0
38 | j = 0
39 | while j < num_plots:
40 | total_psize += pytplot.data_quants[name[j]].attrs['plot_options']['extras']['panel_size']
41 | j += 1
42 |
43 | p_to_use = pytplot.tplot_opt_glob['window_size'][1]/total_psize
44 |
45 | # Create all plots
46 | while i < num_plots:
47 | last_plot = (i == num_plots-1)
48 |
49 | p_height = int(pytplot.data_quants[name[i]].attrs['plot_options']['extras']['panel_size'] * p_to_use)
50 | p_width = pytplot.tplot_opt_glob['window_size'][0]
51 |
52 | # Check plot type
53 | new_fig = _get_figure_class(name[i], auto_color=auto_color, slice=slice, show_xaxis=last_plot)
54 |
55 | new_fig.setsize(height=p_height, width=p_width)
56 |
57 | if i == 0:
58 | new_fig.add_title()
59 |
60 | axis_types.append(new_fig.getaxistype())
61 |
62 | new_fig.buildfigure()
63 |
64 | # Change background color to black if option for it is set - CHECK ONCE WE'VE UPGRADED OUR BOKEH THAT THIS IS
65 | # THE RIGHT WAY TO DO THIS
66 | if pytplot.tplot_opt_glob['black_background']:
67 | new_fig.background_fill_color = '#000000'
68 | else:
69 | new_fig.background_fill_color = '#FFFFFF'
70 |
71 | # Add name of variable to output file name
72 | if last_plot:
73 | out_name += name[i]
74 | else:
75 | out_name += name[i] + '+'
76 |
77 | # Add plot to GridPlot layout
78 | all_plots.append(new_fig.getfig())
79 | i = i+1
80 |
81 | # Add the time stamp to the stack
82 | total_string = ""
83 | if 'time_stamp' in pytplot.extra_layouts:
84 | total_string = pytplot.extra_layouts['time_stamp']
85 |
86 | ts = TimeStamp(text=total_string)
87 | pytplot.extra_layouts['data_time'] = ts
88 | all_plots.append([pytplot.extra_layouts['data_time']])
89 |
90 | # Add extra x axes if applicable
91 | if var_label is not None:
92 | if not isinstance(var_label, list):
93 | var_label = [var_label]
94 | x_axes = []
95 | x_axes_index = 0
96 | for new_x_axis in var_label:
97 | # TODO: Bokeh only handles linear plots for now!!! Fix?!
98 | axis_data_quant = pytplot.data_quants[new_x_axis]
99 | axis_start = np.float(axis_data_quant.min(skipna=True).values)
100 | axis_end = np.float(axis_data_quant.max(skipna=True).values)
101 | x_axes.append(Range1d(start=axis_start, end=axis_end))
102 | k = 0
103 | while k < num_plots:
104 | all_plots[k][0].extra_x_ranges['extra_'+str(new_x_axis)] = x_axes[x_axes_index]
105 | k += 1
106 | all_plots[k-1][0].add_layout(LinearAxis(x_range_name='extra_'+str(new_x_axis)), 'below')
107 | all_plots[k-1][0].plot_height += 22
108 | x_axes_index += 1
109 |
110 | # Set all plots' x_range and plot_width to that of the bottom plot
111 | # so all plots will pan and be resized together.
112 | first_type = {}
113 | if combine_axes:
114 | k = 0
115 | while k < len(axis_types):
116 | if axis_types[k][0] not in first_type:
117 | first_type[axis_types[k][0]] = k
118 | else:
119 | all_plots[k][0].x_range = all_plots[first_type[axis_types[k][0]]][0].x_range
120 | if axis_types[k][1]:
121 | all_plots[k][0].y_range = all_plots[first_type[axis_types[k][0]]][0].y_range
122 | k += 1
123 |
124 | return gridplot(all_plots)
125 |
126 |
127 | def _get_figure_class(tvar_name, auto_color=True, slice=False, show_xaxis=True):
128 | if 'plotter' in pytplot.data_quants[tvar_name].attrs['plot_options']['extras'] \
129 | and pytplot.data_quants[tvar_name].attrs['plot_options']['extras']['plotter'] in pytplot.bokeh_plotters:
130 | cls = pytplot.bokeh_plotters[pytplot.data_quants[tvar_name].attrs['plot_options']['extras']['plotter']]
131 | else:
132 | spec_keyword = pytplot.data_quants[tvar_name].attrs['plot_options']['extras'].get('spec', False)
133 | alt_keyword = pytplot.data_quants[tvar_name].attrs['plot_options']['extras'].get('alt', False)
134 | map_keyword = pytplot.data_quants[tvar_name].attrs['plot_options']['extras'].get('map', False)
135 | if spec_keyword:
136 | cls = pytplot.bokeh_plotters['bkTVarFigureSpec']
137 | elif alt_keyword:
138 | cls = pytplot.bokeh_plotters['bkTVarFigureAlt']
139 | elif map_keyword:
140 | cls = pytplot.bokeh_plotters['bkTVarFigureMap']
141 | else:
142 | cls = pytplot.bokeh_plotters['bkTVarFigure1D']
143 | return cls(tvar_name, auto_color=auto_color, slice=slice, show_xaxis=show_xaxis)
144 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomAxis/BlankAxis.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 |
3 |
4 | class BlankAxis(pg.AxisItem):
5 | #Will need to override other functions in the future for this one
6 | #Right now it chooses weird stupid places for ticks
7 | def __init__(self, orientation, pen=None, linkView=None, parent=None, maxTickLength=-5, showValues=True):
8 | pg.AxisItem.__init__(self, orientation=orientation, pen=pen, linkView=linkView, parent=parent, maxTickLength=maxTickLength, showValues=showValues)
9 |
10 | def tickStrings(self, values, scale, spacing):
11 | strns = []
12 | for _ in values:
13 | try:
14 | strns.append('')
15 | except ValueError:
16 | strns.append('')
17 | return strns
18 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomAxis/DateAxis.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 | import datetime
3 |
4 | class DateAxis(pg.AxisItem):
5 | """
6 | This class takes in tplot time variables and creates ticks/tick labels
7 | depending on the time length of the data.
8 | """
9 | def tickStrings(self, values, scale, spacing):
10 | strns = []
11 | if not values:
12 | return strns
13 | for x in values:
14 | try:
15 | rng = max(values)-min(values)
16 | if rng < 0.001:
17 | string = '%f'
18 | label1 = '%b %d -'
19 | label2 = ' %b %d, %Y'
20 | strns.append(str(round(int(datetime.datetime.utcfromtimestamp(x).strftime(string))/1000000, 6)))
21 | continue
22 | # less than 1 sec of data
23 | elif 0.001 <= rng < 1:
24 | string = '%f'
25 | label1 = '%b %d -'
26 | label2 = ' %b %d, %Y'
27 | strns.append(str(round(int(datetime.datetime.utcfromtimestamp(x).strftime(string))/1000000, 3)))
28 | continue
29 | # Less than five minutes' worth of data
30 | elif 1 <= rng < 300:
31 | # Show hour, min, and sec.
32 | string = '%H:%M:%S'
33 | label1 = '%b %d -'
34 | label2 = ' %b %d, %Y'
35 | # Between five minutes' and four days' worth of data
36 | elif 300 <= rng < 3600*24*4:
37 | # If a new day (some day at 00:00:00 UTC), go ahead and actually
38 | # write out the date
39 | if x % 86400 == 0:
40 | # show YYYY-MM-DD
41 | string = '%Y-%m-%d'
42 | label1 = '%b %d -'
43 | label2 = ' %b %d, %Y'
44 | else:
45 | # Just show hour and min.
46 | string = '%H:%M'
47 | label1 = '%b %d -'
48 | label2 = ' %b %d, %Y'
49 | # Between four days' worth of data and ~ 4 months of data
50 | elif 3600*24*4 <= rng < 4*3600*24*30:
51 | # To keep things uncrowded, just putting month & day, and not the hour/min as well
52 | string = '%m-%d'
53 | label1 = '%b %d -'
54 | label2 = ' %b %d, %Y'
55 | # Between ~ 4 months worth of data and two years' of worth of data
56 | elif 4*3600*24*30 <= rng < 3600*24*30*24:
57 | # Show abbreviated month name and full year (YYYY)
58 | string = '%b-%Y'
59 | label1 = '%Y -'
60 | label2 = ' %Y'
61 | # Greater than two years' worth of data
62 | elif rng >= 3600*24*30*24:
63 | # Just show the year (YYYY)
64 | string = '%Y'
65 | label1 = ''
66 | label2 = ''
67 | strns.append(datetime.datetime.utcfromtimestamp(x).strftime(string))
68 | except ValueError:
69 | # Windows can't handle dates before 1970
70 | strns.append(' ')
71 |
72 | return strns
73 |
74 | def tickSpacing(self, minVal, maxVal, size):
75 | rng = maxVal - minVal
76 | levels = [(1, 0)]
77 | if rng < 0.001:
78 | levels = [(0.0001, 0)]
79 | elif 0.001 <= rng < 0.01:
80 | levels = [(0.001, 0)]
81 | elif 0.01 <= rng < 0.2:
82 | levels = [(0.01, 0)]
83 | elif 0.2 <= rng < 1:
84 | levels = [(0.1, 0)]
85 | elif 1 <= rng < 3:
86 | levels = [(0.5, 0)]
87 | elif 3 <= rng < 4:
88 | # Show ticks every second if you're looking at < four seconds' worth of data
89 | levels = [(1, 0)]
90 | elif 4 <= rng < 15:
91 | # Show ticks every two seconds if you're looking between four and 15 seconds' worth of data
92 | levels = [(2, 0)]
93 | elif 15 <= rng < 60:
94 | # Show ticks every five seconds if you're looking between 15 seconds' and one minutes' worth of data
95 | levels = [(5, 0)]
96 | elif 60 <= rng < 300:
97 | # Show ticks every 30 seconds if you're looking between 1 and 5 minutes worth of data
98 | levels = [(30, 0)]
99 | elif 300 <= rng < 600:
100 | # Show ticks every minute if you're looking between 5 and 10 minutes worth of data
101 | levels = [(60, 0)]
102 | elif 600 <= rng < 1800:
103 | # Show ticks every 5 minutes if you're looking between 10 and 30 minutes worth of data
104 | levels = [(300, 0)]
105 | elif 1800 <= rng < 3600:
106 | # Show ticks every 15 minutes if you're looking between 30 minutes' and one hours' worth of data
107 | levels = [(900, 0)]
108 | elif 3600 <= rng < 3600*2:
109 | # Show ticks every 30 minutes if you're looking between one and two hours' worth of data
110 | levels = [(1800, 0)]
111 | elif 3600*2 <= rng < 3600*6:
112 | # Show ticks every hour if you're looking between two and six hours' worth of data
113 | levels = [(3600, 0)]
114 | elif 3600*6 <= rng < 3600*12:
115 | # Show ticks every two hours if you're looking between six and 12 hours' worth of data
116 | levels = [(7200, 0)]
117 | elif 3600*12 <= rng < 3600*24:
118 | # Show ticks every four hours if you're looking between 12 hours' and one days' worth of data
119 | levels = [(14400, 0)]
120 | elif 3600*24 <= rng < 3600*24*2:
121 | # Show ticks every six hours if you're looking at between one and two days' worth of data
122 | levels = [(21600, 0)]
123 | elif 3600*24*2 <= rng < 3600*24*4:
124 | # show ticks every 12 hours if you're looking between two and four days' worth of data
125 | levels = [(43200, 0)]
126 | elif 3600*24*4 <= rng < 3600*24*30:
127 | # show ticks every two days if data between four days and ~ 1 month
128 | levels = [(172800, 0)]
129 | elif 3600*24*30 <= rng < 2.0*3600*24*30:
130 | # show ticks every four days if data between 1 month and 2 months
131 | levels = [(345600, 0)]
132 | elif 2*3600*24*30 <= rng < 4*3600*24*30:
133 | # show ticks every 7 days if data between 2 month and 4 months
134 | levels = [(604800, 0)]
135 | elif 4*3600*24*30 <= rng < 3600*24*30*24:
136 | # show ticks ~ every month if data between ~ 4 months and ~ two years
137 | levels = [(2.592e+6, 0)]
138 | elif rng >= 3600*24*30*24:
139 | # show ticks ~ every year if data > two years
140 | # show ~ every year
141 | levels = [(3.154e+7, 0)]
142 | return levels
143 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomAxis/NonLinearAxis.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 |
3 |
4 | class NonLinearAxis(pg.AxisItem):
5 |
6 | def __init__(self, orientation, pen=None, linkView=None, parent=None, maxTickLength=-5, showValues=True, mapping_function=None, num_ticks=4):
7 | pg.AxisItem.__init__(self, orientation=orientation, pen=pen, linkView=linkView, parent=parent, maxTickLength=maxTickLength, showValues=showValues)
8 | self.f = mapping_function
9 | self.num_ticks = num_ticks
10 |
11 | def tickStrings(self, values, scale, spacing):
12 | strns = []
13 | for x in values:
14 | try:
15 | strns.append(str(int(self.f(x))))
16 | except ValueError:
17 | strns.append('')
18 | return strns
19 |
20 | def tickValues(self, minVal, maxVal, size):
21 | minVal, maxVal = sorted((minVal, maxVal))
22 | minVal *= self.scale
23 | maxVal *= self.scale
24 | ticks=[]
25 | xrange = maxVal - minVal
26 | for i in range(0, self.num_ticks+1):
27 | ticks.append(minVal+(i*xrange/self.num_ticks))
28 | return [(1.0,ticks)]
29 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomAxis/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/QtPlotter/CustomAxis/__init__.py
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomImage/ColorbarImage.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 | from pyqtgraph.Qt import QtCore
3 | import numpy as np
4 | import collections
5 | from pyqtgraph import functions as fn
6 | from pyqtgraph import debug as debug
7 | from pyqtgraph.Point import Point
8 | from collections.abc import Callable
9 |
10 | class ColorbarImage(pg.ImageItem):
11 | '''
12 | For the most part, this class is exactly the same as pg.ImageItem.
13 |
14 | This exist literally only because collections.Callable became collections.abc.Callable
15 | and it was causing errors.
16 | '''
17 |
18 | def render(self):
19 | # Convert data to QImage for display.
20 |
21 | profile = debug.Profiler()
22 | if self.image is None or self.image.size == 0:
23 | return
24 | if isinstance(self.lut, collections.abc.Callable):
25 | lut = self.lut(self.image)
26 | else:
27 | lut = self.lut
28 |
29 | if self.autoDownsample:
30 | # reduce dimensions of image based on screen resolution
31 | o = self.mapToDevice(QtCore.QPointF(0, 0))
32 | x = self.mapToDevice(QtCore.QPointF(1, 0))
33 | y = self.mapToDevice(QtCore.QPointF(0, 1))
34 | w = Point(x - o).length()
35 | h = Point(y - o).length()
36 | if w == 0 or h == 0:
37 | self.qimage = None
38 | return
39 | xds = max(1, int(1.0 / w))
40 | yds = max(1, int(1.0 / h))
41 | axes = [1, 0] if self.axisOrder == 'row-major' else [0, 1]
42 | image = fn.downsample(self.image, xds, axis=axes[0])
43 | image = fn.downsample(image, yds, axis=axes[1])
44 | self._lastDownsample = (xds, yds)
45 | else:
46 | image = self.image
47 |
48 | # if the image data is a small int, then we can combine levels + lut
49 | # into a single lut for better performance
50 | levels = self.levels
51 | if levels is not None and levels.ndim == 1 and image.dtype in (np.ubyte, np.uint16):
52 | if self._effectiveLut is None:
53 | eflsize = 2 ** (image.itemsize * 8)
54 | ind = np.arange(eflsize)
55 | minlev, maxlev = levels
56 | levdiff = maxlev - minlev
57 | levdiff = 1 if levdiff == 0 else levdiff # don't allow division by 0
58 | if lut is None:
59 | efflut = fn.rescaleData(ind, scale=255. / levdiff,
60 | offset=minlev, dtype=np.ubyte)
61 | else:
62 | lutdtype = np.min_scalar_type(lut.shape[0] - 1)
63 | efflut = fn.rescaleData(ind, scale=(lut.shape[0] - 1) / levdiff,
64 | offset=minlev, dtype=lutdtype, clip=(0, lut.shape[0] - 1))
65 | efflut = lut[efflut]
66 |
67 | self._effectiveLut = efflut
68 | lut = self._effectiveLut
69 | levels = None
70 |
71 | # Assume images are in column-major order for backward compatibility
72 | # (most images are in row-major order)
73 |
74 | if self.axisOrder == 'col-major':
75 | image = image.transpose((1, 0, 2)[:image.ndim])
76 |
77 | argb, alpha = fn.makeARGB(image, lut=lut, levels=levels)
78 | self.qimage = fn.makeQImage(argb, alpha, transpose=False)
79 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomImage/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/QtPlotter/CustomImage/__init__.py
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomLegend/CustomLegend.py:
--------------------------------------------------------------------------------
1 |
2 | import pyqtgraph as pg
3 |
4 | from pyqtgraph import GraphicsWidget
5 | from pyqtgraph import LabelItem
6 | from pyqtgraph import functions as fn
7 | from pyqtgraph.Point import Point
8 |
9 | __all__ = ['LegendItem']
10 |
11 | class CustomLegendItem(pg.LegendItem):
12 |
13 | def addItem(self,name1,name2):
14 | label1 = LabelItem(name1)
15 | label2 = LabelItem(name2)
16 | row = self.layout.rowCount()
17 | self.items.append((label1, label2))
18 | self.layout.addItem(label1, row, 0)
19 | self.layout.addItem(label2, row, 1)
20 | self.updateSize()
21 |
22 | def removeItem(self, name):
23 | for label, data in self.items:
24 | if label.text == name: # hit
25 | self.items.remove( (label, data) ) # remove from itemlist
26 | self.layout.removeItem(label) # remove from layout
27 | label.close() # remove from drawing
28 | self.layout.removeItem(data)
29 | data.close()
30 | self.updateSize() # redraq box
31 |
32 | def setItem(self, label_name, new_data):
33 | for label, data in self.items:
34 | if label.text == label_name:
35 | data.setText(new_data)
36 | return
37 | self.addItem(label_name, new_data)
38 |
39 | def paint(self, p, *args):
40 | p.setPen(fn.mkPen(255,255,255,0))
41 | p.setBrush(fn.mkBrush(0,0,0,190))
42 | p.drawRect(self.boundingRect())
43 |
44 | def hoverEvent(self, ev):
45 | #ev.acceptDrags(QtCore.Qt.LeftButton)
46 | return
47 |
48 | def mouseDragEvent(self, ev):
49 | return
50 | #if ev.button() == QtCore.Qt.LeftButton:
51 | # dpos = ev.pos() - ev.lastPos()
52 | # self.autoAnchor(self.pos() + dpos)
53 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomLegend/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/QtPlotter/CustomLegend/__init__.py
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomLinearRegionItem/CustomLinearRegionItem.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 | from pyqtgraph import functions as fn
3 |
4 |
5 | class CustomLinearRegionItem(pg.LinearRegionItem):
6 | """Inheriting the LinearRegionItem class because if someone hovers over a region of interest box,
7 | I don't want it to have a different alpha value than when the mouse is not over the box."""
8 |
9 | def setMouseHover(self, hover):
10 | # Inform the item that the mouse is(not) hovering over it
11 | if self.mouseHovering == hover:
12 | return
13 | self.mouseHovering = hover
14 | if hover:
15 | c = self.brush.color()
16 | c.setAlpha(c.alpha() * 1)
17 | self.currentBrush = fn.mkBrush(c)
18 | else:
19 | self.currentBrush = self.brush
20 | self.update()
21 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomViewBox/CustomVB.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 |
3 |
4 | class CustomVB(pg.ViewBox):
5 |
6 | def mouseDragEvent(self, ev, axis=None):
7 | return
8 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomViewBox/NoPaddingPlot.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 |
3 |
4 | class NoPaddingPlot(pg.ViewBox):
5 |
6 | def suggestPadding(self, axis):
7 | l = self.width() if axis == 0 else self.height()
8 | if l > 0:
9 | padding = 0.002
10 | else:
11 | padding = 0.002
12 | return padding
--------------------------------------------------------------------------------
/pytplot/QtPlotter/CustomViewBox/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/QtPlotter/CustomViewBox/__init__.py
--------------------------------------------------------------------------------
/pytplot/QtPlotter/PyTPlot_Exporter.py:
--------------------------------------------------------------------------------
1 | import pyqtgraph as pg
2 | import numpy as np
3 | import pyqtgraph.functions as fn
4 | from pyqtgraph.Qt import QtCore, QtGui
5 | from pyqtgraph.exporters import Exporter
6 | from pyqtgraph.parametertree import Parameter
7 |
8 |
9 | class PytplotExporter(pg.exporters.ImageExporter):
10 |
11 | def __init__(self, item):
12 | Exporter.__init__(self, item)
13 | tr = self.getTargetRect()
14 | if isinstance(item, QtGui.QGraphicsItem):
15 | scene = item.scene()
16 | else:
17 | scene = item
18 | # CHANGE: Used to be scene.views()[0].backgroundBrush()
19 | # That wasn't how to access the background of a GraphicsLayout object
20 | bgbrush = scene.backgroundBrush()
21 | bg = bgbrush.color()
22 | if bgbrush.style() == QtCore.Qt.NoBrush:
23 | bg.setAlpha(0)
24 |
25 | self.params = Parameter(name='params', type='group', children=[
26 | {'name': 'width', 'type': 'int', 'value': tr.width(), 'limits': (0, None)},
27 | {'name': 'height', 'type': 'int', 'value': tr.height(), 'limits': (0, None)},
28 | {'name': 'antialias', 'type': 'bool', 'value': True},
29 | {'name': 'background', 'type': 'color', 'value': bg},
30 | ])
31 |
32 | self.params.param('width').sigValueChanged.connect(self.widthChanged)
33 | self.params.param('height').sigValueChanged.connect(self.heightChanged)
34 |
35 | def export(self, fileName=None, toBytes=False, copy=False):
36 | if fileName is None and not toBytes and not copy:
37 | if pg.Qt.USE_PYSIDE:
38 | filter = ["*." + str(f) for f in QtGui.QImageWriter.supportedImageFormats()]
39 | else:
40 | filter = ["*." + bytes(f).decode('utf-8') for f in QtGui.QImageWriter.supportedImageFormats()]
41 | preferred = ['*.png', '*.tif', '*.jpg']
42 | for p in preferred[::-1]:
43 | if p in filter:
44 | filter.remove(p)
45 | filter.insert(0, p)
46 | self.fileSaveDialog(filter=filter)
47 | return
48 |
49 | targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height'])
50 | sourceRect = self.getSourceRect()
51 |
52 |
53 | # self.png = QtGui.QImage(targetRect.size(), QtGui.QImage.Format_ARGB32)
54 | # self.png.fill(pyqtgraph.mkColor(self.params['background']))
55 | w, h = self.params['width'], self.params['height']
56 | if w == 0 or h == 0:
57 | raise Exception("Cannot export image with size=0 (requested export size is %dx%d)" % (w, h))
58 | bg = np.empty((int(self.params['width']), int(self.params['height']), 4), dtype=np.ubyte)
59 | color = self.params['background']
60 | bg[:, :, 0] = color.blue()
61 | bg[:, :, 1] = color.green()
62 | bg[:, :, 2] = color.red()
63 | bg[:, :, 3] = color.alpha()
64 | self.png = fn.makeQImage(bg, alpha=True)
65 |
66 | # set resolution of image:
67 | origTargetRect = self.getTargetRect()
68 | resolutionScale = targetRect.width() / origTargetRect.width()
69 |
70 | painter = QtGui.QPainter(self.png)
71 | # dtr = painter.deviceTransform()
72 | try:
73 | self.setExportMode(True,
74 | {'antialias': self.params['antialias'], 'background': self.params['background'],
75 | 'painter': painter, 'resolutionScale': resolutionScale})
76 | painter.setRenderHint(QtGui.QPainter.Antialiasing, self.params['antialias'])
77 | # CHANGE: Rendering the scence twice onto the QImage. The first time, make it one pixel in size.
78 | # Next, render the full thing. No idea why we need to render is twice, but we do.
79 | self.getScene().render(painter, QtCore.QRectF(0, 0, 1, 1), QtCore.QRectF(0, 0, 1, 1))
80 | self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))
81 | finally:
82 | self.setExportMode(False)
83 | painter.end()
84 |
85 | if copy:
86 | QtGui.QApplication.clipboard().setImage(self.png)
87 | elif toBytes:
88 | return self.png
89 | else:
90 | self.png.save(fileName)
91 |
92 | def getPaintItems(self, root=None):
93 | """Return a list of all items that should be painted in the correct order."""
94 | if root is None:
95 | root = self.item
96 | preItems = []
97 | postItems = []
98 | if isinstance(root, QtGui.QGraphicsScene):
99 | childs = [i for i in root.items() if i.parentItem() is None]
100 | rootItem = []
101 | else:
102 | # CHANGE: For GraphicsLayouts, there is no function for childItems(), so I just
103 | # replaced it with .items()
104 | try:
105 | childs = root.childItems()
106 | except:
107 | childs = root.items()
108 | rootItem = [root]
109 | childs.sort(key=lambda a: a.zValue())
110 | while len(childs) > 0:
111 | ch = childs.pop(0)
112 | tree = self.getPaintItems(ch)
113 |
114 | if int(ch.flags() & ch.ItemStacksBehindParent) > 0 or (
115 | ch.zValue() < 0 and int(ch.flags() & ch.ItemNegativeZStacksBehindParent) > 0):
116 | preItems.extend(tree)
117 | else:
118 | postItems.extend(tree)
119 | return preItems + rootItem + postItems
120 |
121 | def getTargetRect(self):
122 | # CHANGE: Used to return self.item.sceneBoundingRect(). GraphicsLayouts don't have a
123 | # sceneBoundingRect(), but they have a rect() which appears to work just as well.
124 | return self.item.rect()
125 |
126 | def getSourceRect(self):
127 | # CHANGE: Used to return self.item.mapRectToDevice(self.item.boundingRect()). GraphicsLayouts don't have a
128 | # sceneBoundingRect() OR a mapRectToDevice, but they have a rect() which appears to work just as well.
129 | return self.item.rect()
130 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/TVarFigureAxisOnly.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | import pyqtgraph as pg
8 | from pyqtgraph.Qt import QtCore, QtGui
9 | from scipy import interpolate
10 | from .CustomAxis.NonLinearAxis import NonLinearAxis
11 | from .CustomViewBox.CustomVB import CustomVB
12 | from .CustomAxis.AxisItem import AxisItem
13 |
14 |
15 | class TVarFigureAxisOnly(pg.GraphicsLayout):
16 | def __init__(self, tvar_name):
17 | self.tvar_name = tvar_name
18 |
19 | # Sets up the layout of the Tplot Object
20 | pg.GraphicsLayout.__init__(self)
21 | self.layout.setHorizontalSpacing(10)
22 | self.layout.setContentsMargins(0, 0, 0, 0)
23 |
24 | if pytplot.tplot_opt_glob['black_background']:
25 | self.labelStyle = {'font-size':
26 | str(pytplot.data_quants[self.tvar_name].attrs['plot_options']['extras']['char_size'])
27 | + 'pt', 'color': '#FFF'}
28 | else:
29 | self.labelStyle = {'font-size':
30 | str(pytplot.data_quants[self.tvar_name].attrs['plot_options']['extras']['char_size'])
31 | + 'pt', 'color': '#000'}
32 |
33 | vb = CustomVB(enableMouse=False)
34 | self.yaxis = AxisItem("left")
35 | self.yaxis.setLabel(pytplot.data_quants[self.tvar_name].attrs['plot_options']['yaxis_opt']['axis_label'], **self.labelStyle)
36 | self.yaxis.label.setRotation(0)
37 | qt_transform = pg.QtGui.QTransform()
38 | qt_transform.translate(0,-40)
39 | self.yaxis.label.setTransform(qt_transform)
40 | #self.yaxis.label.translate(0, -40)
41 | mapping_function = interpolate.interp1d(pytplot.data_quants[self.tvar_name].coords['time'].values, pytplot.data_quants[self.tvar_name].values)
42 | if 'var_label_ticks' in pytplot.data_quants[self.tvar_name].attrs['plot_options']:
43 | num_ticks = pytplot.data_quants[self.tvar_name].attrs['plot_options']['var_label_ticks']
44 | else:
45 | num_ticks=5
46 | xaxis = NonLinearAxis(orientation='bottom', mapping_function=mapping_function, num_ticks=num_ticks)
47 |
48 | # Set the font size of the axes
49 | font = QtGui.QFont()
50 | font.setPixelSize(pytplot.tplot_opt_glob['axis_font_size'])
51 | xaxis.setTickFont(font)
52 |
53 | self.plotwindow = self.addPlot(row=0, col=0, axisItems={'bottom': xaxis, 'left': self.yaxis}, viewBox=vb, colspan=1)
54 | self.plotwindow.buttonsHidden = True
55 | self.plotwindow.setMaximumHeight(20)
56 |
57 | # Set up the view box needed for the legends
58 | self.legendvb = pg.ViewBox(enableMouse=False)
59 | self.legendvb.setMaximumWidth(100)
60 | self.addItem(self.legendvb, 0, 1)
61 |
--------------------------------------------------------------------------------
/pytplot/QtPlotter/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from .TVarFigure1D import TVarFigure1D
7 | from .TVarFigureAlt import TVarFigureAlt
8 | from .TVarFigureAxisOnly import TVarFigureAxisOnly
9 | from .TVarFigureSpec import TVarFigureSpec
10 | from .TVarFigureMap import TVarFigureMap
11 | from .generate import generate_stack
12 | try:
13 | from .PyTPlot_Exporter import PytplotExporter
14 | except:
15 | pass
--------------------------------------------------------------------------------
/pytplot/QtPlotter/generate.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 |
7 | from __future__ import division
8 | import pytplot
9 | from pyqtgraph import LabelItem
10 | import pyqtgraph as pg
11 | from .TVarFigureAxisOnly import TVarFigureAxisOnly
12 |
13 |
14 | def generate_stack(name,
15 | var_label=None,
16 | combine_axes=True,
17 | vert_spacing=25):
18 |
19 | # Set plot backgrounds to black if that tplot_option is set
20 | if pytplot.tplot_opt_glob['black_background']:
21 | pg.setConfigOptions(background='k')
22 | else:
23 | pg.setConfigOptions(background='w')
24 |
25 | new_stack = pg.GraphicsLayoutWidget()
26 |
27 | # Variables needed for pyqtgraph plots
28 | xaxis_thickness = 35
29 | varlabel_xaxis_thickness = 20
30 | title_thickness = 50
31 |
32 | # Setting up the pyqtgraph window
33 | new_stack.setWindowTitle(pytplot.tplot_opt_glob['title_text'])
34 | new_stack.resize(pytplot.tplot_opt_glob['window_size'][0], pytplot.tplot_opt_glob['window_size'][1])
35 |
36 | # Vertical Box layout to store plots
37 | all_plots = []
38 | axis_types = []
39 | i = 0
40 | num_plots = len(name)
41 |
42 | # Configure plot sizes
43 | total_psize = 0
44 | j = 0
45 | while j < num_plots:
46 | total_psize += pytplot.data_quants[name[j]].attrs['plot_options']['extras']['panel_size']
47 | j += 1
48 |
49 | if var_label is not None:
50 | if not isinstance(var_label, list):
51 | var_label = [var_label]
52 | varlabel_correction = len(var_label) * varlabel_xaxis_thickness
53 | else:
54 | varlabel_correction = 0
55 |
56 | p_to_use = \
57 | (pytplot.tplot_opt_glob['window_size'][1] - xaxis_thickness - title_thickness - varlabel_correction) / total_psize
58 |
59 | # Whether or not there is a title row in pyqtgraph
60 | titlerow = 0
61 | spacing_in_pixels = vert_spacing
62 | new_stack.ci.layout.setSpacing(spacing_in_pixels)
63 |
64 | # Create all plots
65 | while i < num_plots:
66 | last_plot = (i == num_plots - 1)
67 |
68 | p_height = int(pytplot.data_quants[name[i]].attrs['plot_options']['extras']['panel_size'] * p_to_use)
69 |
70 | if last_plot:
71 | p_height += xaxis_thickness
72 | if i == 0:
73 | if _set_pyqtgraph_title(new_stack):
74 | titlerow = 1
75 | new_stack.ci.layout.setRowPreferredHeight(i + titlerow, p_height)
76 | new_fig = _get_figure_class(name[i], show_xaxis=last_plot)
77 |
78 | new_stack.addItem(new_fig, row=i + titlerow, col=0)
79 |
80 | axis_types.append(new_fig.getaxistype())
81 | new_fig.buildfigure()
82 |
83 | # Add plot to GridPlot layout
84 | all_plots.append(new_fig.getfig())
85 |
86 | i = i + 1
87 |
88 | # Add extra x axes if applicable
89 | if var_label is not None:
90 | x_axes_index = 0
91 | for new_x_axis in var_label:
92 | new_axis = TVarFigureAxisOnly(new_x_axis)
93 | new_stack.addItem(new_axis, row=num_plots + titlerow + x_axes_index, col=0)
94 | x_axes_index += 1
95 | axis_types.append(('time', False))
96 | all_plots.append(new_axis)
97 |
98 | # Ensure all plots in the stack have the same y axis width
99 | maximum_y_axis_width = 0
100 | for plot in all_plots:
101 | if maximum_y_axis_width < plot.yaxis.getWidth():
102 | maximum_y_axis_width = plot.yaxis.getWidth()
103 |
104 | for plot in all_plots:
105 | if 'yaxis_width' in pytplot.tplot_opt_glob:
106 | plot.yaxis.setWidth(pytplot.tplot_opt_glob['yaxis_width'])
107 | else:
108 | plot.yaxis.setWidth(maximum_y_axis_width)
109 |
110 | # Set all plots' x_range and plot_width to that of the bottom plot
111 | # so all plots will pan and be resized together.
112 | first_type = {}
113 | if combine_axes:
114 | k = 0
115 | while k < len(axis_types):
116 | if axis_types[k][0] not in first_type:
117 | first_type[axis_types[k][0]] = k
118 | else:
119 | all_plots[k].plotwindow.setXLink(all_plots[first_type[axis_types[k][0]]].plotwindow)
120 | k += 1
121 |
122 | return new_stack
123 |
124 |
125 | def _set_pyqtgraph_title(layout):
126 | """
127 | Private function to add a title to the first row of the window.
128 | Returns True if a Title is set. Else, returns False.
129 | """
130 | title_set = False
131 | if 'title_size' in pytplot.tplot_opt_glob:
132 | size = pytplot.tplot_opt_glob['title_size']
133 | if 'title_text' in pytplot.tplot_opt_glob:
134 | title_set = True
135 | if pytplot.tplot_opt_glob['title_text'] != '' and pytplot.tplot_opt_glob['black_background']:
136 | layout.addItem(LabelItem(pytplot.tplot_opt_glob['title_text'], size=size, color='w'), row=0, col=0)
137 | else:
138 | layout.addItem(LabelItem(pytplot.tplot_opt_glob['title_text'], size=size, color='k'), row=0, col=0)
139 | return title_set
140 |
141 |
142 | def _get_figure_class(tvar_name, show_xaxis=True):
143 | if 'plotter' in pytplot.data_quants[tvar_name].attrs['plot_options']['extras'] \
144 | and pytplot.data_quants[tvar_name].attrs['plot_options']['extras']['plotter'] in \
145 | pytplot.qt_plotters:
146 | cls = pytplot.qt_plotters[pytplot.data_quants[tvar_name].attrs['plot_options']['extras']['plotter']]
147 | else:
148 | spec_keyword = pytplot.data_quants[tvar_name].attrs['plot_options']['extras'].get('spec', False)
149 | alt_keyword = pytplot.data_quants[tvar_name].attrs['plot_options']['extras'].get('alt', False)
150 | map_keyword = pytplot.data_quants[tvar_name].attrs['plot_options']['extras'].get('map', False)
151 | if spec_keyword:
152 | cls = pytplot.qt_plotters['qtTVarFigureSpec']
153 | elif alt_keyword:
154 | cls = pytplot.qt_plotters['qtTVarFigureAlt']
155 | elif map_keyword:
156 | cls = pytplot.qt_plotters['qtTVarFigureMap']
157 | else:
158 | cls = pytplot.qt_plotters['qtTVarFigure1D']
159 | return cls(tvar_name, show_xaxis=show_xaxis)
160 |
--------------------------------------------------------------------------------
/pytplot/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from _collections import OrderedDict
7 | from . import HTMLPlotter
8 | import os
9 | import sys
10 | import xarray as xr
11 |
12 | # xarray options
13 | xr.set_options(keep_attrs=True)
14 |
15 | # runs without Qt
16 | if not 'PYTPLOT_NO_GRAPHICS' in os.environ:
17 | using_graphics = True
18 | else:
19 | print("Turning off qt graphics. Bokeh plotting is still enabled.")
20 | using_graphics = False
21 |
22 | try:
23 | import google.colab
24 | using_graphics = False
25 | except:
26 | pass
27 |
28 | # This variable will be constantly changed depending on what x value the user is hovering over
29 | class HoverTime(object):
30 | hover_time = 0
31 | functions_to_call = []
32 |
33 | def register_listener(self, fn):
34 | self.functions_to_call.append(fn)
35 | return
36 |
37 | # New time is time we're hovering over, grabbed from TVarFigure(1D/Spec/Alt/Map)
38 | # name is whatever tplot we're currently hovering over (relevant for 2D interactive
39 | # plots that appear when plotting a spectrogram).
40 | def change_hover_time(self, new_time, name=None):
41 | self.hover_time = new_time
42 | for f in self.functions_to_call:
43 | try:
44 | f(self.hover_time, name)
45 | except Exception as e:
46 | print(e)
47 | return
48 |
49 | if using_graphics:
50 | try:
51 | import pyqtgraph as pg
52 | from pyqtgraph.Qt import QtWidgets
53 |
54 | #Note: This is absolutely required for windows systems to work currently
55 | #But presumably it will be fixed at some point in the future
56 | if sys.platform.startswith('win'):
57 | pg.ptime.time = lambda: 0
58 |
59 | pg.setConfigOptions(imageAxisOrder='row-major')
60 | pg.setConfigOptions(background='w')
61 |
62 |
63 | class PlotWindow(QtWidgets.QMainWindow):
64 | def __init__(self):
65 | super().__init__()
66 |
67 | def init_savepng(self, exporter):
68 | if exporter is None:
69 | return
70 | # Set up the save PNG button/call exportpng function that activates when user presses button
71 | exportdatapngaction = QtWidgets.QAction("Save PNG", self)
72 | exportdatapngaction.triggered.connect(lambda: self.exportpng(exporter))
73 |
74 | # Set up menu bar to display and call creation of save PNG button
75 | menubar = self.menuBar()
76 | menubar.setNativeMenuBar(False)
77 | menubar.addAction(exportdatapngaction)
78 | self.setWindowTitle('PyTplot Window')
79 |
80 | def exportpng(self, exporter):
81 | if exporter is None:
82 | print("Cannot save the image. Try installing h5py to get around this issue.")
83 | return
84 | # Function called by save PNG button to grab the image from the plot window and save it
85 | fname = QtWidgets.QFileDialog.getSaveFileName(self, 'Open file', 'pytplot.png', filter="png (*.png *.)")
86 | exporter.parameters()['width'] = tplot_opt_glob['window_size'][0]
87 | exporter.parameters()['height'] = tplot_opt_glob['window_size'][1]
88 | exporter.export(fname[0])
89 |
90 | def newlayout(self, layout):
91 | # Needed for displaying plots
92 | self.setCentralWidget(layout)
93 |
94 | except Exception as e:
95 | print("Qt graphics import failed with error " + str(e))
96 | print("Turning off qt graphics. Bokeh plotting is still enabled.")
97 | using_graphics = False
98 |
99 | # Global Variables
100 | hover_time = HoverTime()
101 | data_quants = OrderedDict()
102 | interactive_window = None # 2D interactive window that appears whenever plotting spectrograms w/ tplot.
103 | # If option 't_average' is set by user, then x and y values on this plot are the average of the user-specified
104 | # number of seconds for which the cursor location should be averaged.
105 | static_window = None # 2D window showing data at certain point in time from a spectrogram plot.
106 | static_tavg_window = None # 2D window showing averaged y and z data for a specified time range from a spectrogram plot.
107 | tplot_opt_glob = dict(tools="xpan,crosshair,reset",
108 | min_border_top=12, min_border_bottom=12,
109 | title_align='center', window_size=[800, 800],
110 | title_size='12pt', title_text='', crosshair=True,
111 | data_gap=0, black_background=False, axis_font_size=12, axis_tick_num=[(0, 1000000000), (3, 1),],
112 | y_axis_zoom=False)
113 | lim_info = {}
114 | extra_layouts = {}
115 |
116 | if using_graphics:
117 | pytplotWindow_names = []
118 | pytplotWindows = [] # This is a list that will hold future qt windows
119 | from . import QtPlotter
120 |
121 | qt_plotters = {'qtTVarFigure1D': QtPlotter.TVarFigure1D,
122 | 'qtTVarFigureSpec': QtPlotter.TVarFigureSpec,
123 | 'qtTVarFigureAlt': QtPlotter.TVarFigureAlt,
124 | 'qtTVarFigureMap': QtPlotter.TVarFigureMap}
125 |
126 | bokeh_plotters = {'bkTVarFigure1D': HTMLPlotter.TVarFigure1D,
127 | 'bkTVarFigureMap': HTMLPlotter.TVarFigureMap,
128 | 'bkTVarFigureAlt': HTMLPlotter.TVarFigureAlt,
129 | 'bkTVarFigureSpec': HTMLPlotter.TVarFigureSpec}
130 |
131 | from .store_data import store_data
132 | from .tplot import tplot
133 | from .get_data import get_data
134 | from .xlim import xlim
135 | from .ylim import ylim
136 | from .zlim import zlim
137 | from .tlimit import tlimit
138 | from pytplot.exporters.tplot_save import tplot_save
139 | from .tplot_names import tplot_names
140 | from pytplot.importers.tplot_restore import tplot_restore
141 | from .get_timespan import get_timespan
142 | from .tplot_options import tplot_options
143 | from .tplot_rename import tplot_rename
144 | from .tplot_copy import tplot_copy
145 | from .replace_data import replace_data
146 | from .get_ylimits import get_ylimits
147 | from .timebar import timebar
148 | from .del_data import del_data
149 | from .timespan import timespan
150 | from .options import options
151 | from .timestamp import timestamp
152 | from pytplot.importers.cdf_to_tplot import cdf_to_tplot
153 | from pytplot.importers.netcdf_to_tplot import netcdf_to_tplot
154 | from pytplot.importers.sts_to_tplot import sts_to_tplot
155 | from .tplot_utilities import compare_versions
156 | from .link import link
157 | from pytplot.tplot_math import *
158 |
159 | # Start the App
160 | if using_graphics:
161 | try:
162 | pg.mkQApp()
163 | except Exception as e:
164 | print("Qt graphics import failed with error " + str(e))
165 | print("Turning off qt graphics. Bokeh plotting is still enabled.")
166 | using_graphics = False
167 |
168 | # Ok. In possibly the weirdest turn of events, I get a warning that interrupts Qt specplots
169 | # if I DO NOT import this library. There is an error about collections.abc in the ImageItem.render()
170 | # function in pyqtgraph that completely works FINE as long as I've imported this library somewhere before
171 | # that render function being called. Why??
172 | import requests
173 |
--------------------------------------------------------------------------------
/pytplot/del_data.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | import fnmatch
8 |
9 | def del_data(name=None):
10 | """
11 | This function will delete tplot variables that are already stored in memory.
12 |
13 | Parameters:
14 | name : str
15 | Name of the tplot variable to be deleted. If no name is provided, then
16 | all tplot variables will be deleted.
17 |
18 | Returns:
19 | None
20 |
21 | Examples:
22 | >>> # Delete Variable 1
23 | >>> import pytplot
24 | >>> pytplot.del_data("Variable1")
25 |
26 | """
27 | if name is None:
28 | tplot_names = list(pytplot.data_quants.keys())
29 | for i in tplot_names:
30 | del pytplot.data_quants[i]
31 | return
32 |
33 | if not isinstance(name, list):
34 | name = [name]
35 |
36 | entries = []
37 | for i in name:
38 | if ('?' in i) or ('*' in i):
39 | for j in pytplot.data_quants.keys():
40 | if isinstance(pytplot.data_quants[j], dict):
41 | # NRV variable
42 | var_verif = fnmatch.fnmatch(pytplot.data_quants[j]['name'], i)
43 | if var_verif == 1:
44 | entries.append(pytplot.data_quants[j]['name'])
45 | else:
46 | continue
47 | else:
48 | var_verif = fnmatch.fnmatch(pytplot.data_quants[j].name, i)
49 | if var_verif == 1:
50 | entries.append(pytplot.data_quants[j].name)
51 | else:
52 | continue
53 | for key in entries:
54 | if key in pytplot.data_quants:
55 | del pytplot.data_quants[key]
56 | elif i not in pytplot.data_quants.keys():
57 | print(str(i) + " is currently not in pytplot.")
58 | return
59 |
60 | else:
61 | if isinstance(pytplot.data_quants[i], dict):
62 | temp_data_quants = pytplot.data_quants[i]
63 | str_name = temp_data_quants['name']
64 | else:
65 | temp_data_quants = pytplot.data_quants[i]
66 | str_name = temp_data_quants.name
67 |
68 | del pytplot.data_quants[str_name]
69 |
70 | return
--------------------------------------------------------------------------------
/pytplot/exporters/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/exporters/__init__.py
--------------------------------------------------------------------------------
/pytplot/exporters/tplot_ascii.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 |
8 | def tplot_ascii(tvar, filename=None, extension='.csv'):
9 | # grab data, prepend index column
10 | if filename == None:
11 | filename = tvar
12 | # save data
13 | pytplot.data_quants[tvar].to_pandas().to_csv(filename + extension)
14 | # only try to save spec_bins (y-values in spectrograms) if we're sure they exist
15 | if 'spec_bins' in pytplot.data_quants[tvar].coords.keys():
16 | pytplot.data_quants[tvar].coords['spec_bins'].to_pandas().to_csv(filename + '_v' + extension)
17 |
18 |
--------------------------------------------------------------------------------
/pytplot/exporters/tplot_save.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pickle
7 | import pytplot
8 |
9 | def tplot_save(names, filename=None):
10 | """
11 | This function will save tplot variables into a single file by using the python "pickle" function.
12 | This file can then be "restored" using tplot_restore. This is useful if you want to end the pytplot session,
13 | but save all of your data/options. All variables and plot options can be read back into tplot with the
14 | "tplot_restore" command.
15 |
16 | Parameters:
17 | names : str/list
18 | A string or a list of strings of the tplot variables you would like saved.
19 | filename : str, optional
20 | The filename where you want to save the file.
21 |
22 | Returns:
23 | None
24 |
25 | Examples:
26 | >>> # Save a single tplot variable
27 | >>> import pytplot
28 | >>> x_data = [1,2,3,4,5]
29 | >>> y_data = [1,2,3,4,5]
30 | >>> pytplot.store_data("Variable1", data={'x':x_data, 'y':y_data})
31 | >>> pytplot.ylim('Variable1', 2, 4)
32 | >>> pytplot.save('Variable1', filename='C:/temp/variable1.pytplot')
33 |
34 | """
35 | if isinstance(names,int):
36 | names = list(pytplot.data_quants.keys())[names-1]
37 | if not isinstance(names, list):
38 | names = [names]
39 |
40 | #Check that we have all available data
41 | for name in names:
42 | if not isinstance(pytplot.data_quants[name], dict): # not a NRV variable
43 | # variable is a time series
44 | for oplot_name in pytplot.data_quants[name].attrs['plot_options']['overplots']:
45 | if oplot_name not in names:
46 | names.append(oplot_name)
47 |
48 | #Pickle it up
49 | to_pickle =[]
50 | for name in names:
51 | if name not in pytplot.data_quants.keys():
52 | print("That name is currently not in pytplot")
53 | return
54 | to_pickle.append(pytplot.data_quants[name])
55 |
56 | num_quants = len(to_pickle)
57 | to_pickle = [num_quants] + to_pickle
58 | temp_tplot_opt_glob = pytplot.tplot_opt_glob
59 | to_pickle.append(temp_tplot_opt_glob)
60 |
61 | if filename==None:
62 | filename='var_'+'-'.join(names)+'.pytplot'
63 |
64 | out_file = open(filename, "wb")
65 | pickle.dump(to_pickle, out_file)
66 | out_file.close()
67 |
68 | return
--------------------------------------------------------------------------------
/pytplot/get_data.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | from collections import namedtuple
8 |
9 | def get_data(name, xarray=False, metadata=False):
10 | """
11 | This function extracts the data from the tplot Variables stored in memory.
12 |
13 | Parameters:
14 | name : str
15 | Name of the tplot variable
16 | xarray : bool
17 | Keep the variable as an xarray object
18 | metadata : bool
19 | Return the metadata of the object (the attr dictionary) instead of the actual data
20 |
21 |
22 | Returns: tuple of data/dimensions/metadata stored in pytplot
23 | time_val : numpy array of seconds since 1970
24 | data_val : n-dimensional array of data
25 | spec_bins_val (if exists) : spectral bins if the plot is a spectrogram
26 | v1_val (if exists) : numpy array of v1 dimension coordinates
27 | v2_val {if exists} : numpy array of v2 dimension coordinates
28 | v3_val (if exists) : numpy array of v3 dimension coordinates
29 |
30 |
31 | Examples:
32 | >>> # Retrieve the data from Variable 1
33 | >>> import pytplot
34 | >>> x_data = [1,2,3,4,5]
35 | >>> y_data = [1,2,3,4,5]
36 | >>> pytplot.store_data("Variable1", data={'x':x_data, 'y':y_data})
37 | >>> time, data = pytplot.get_data("Variable1")
38 |
39 | """
40 |
41 | if name not in pytplot.data_quants.keys():
42 | print("That name is currently not in pytplot")
43 | return
44 |
45 | temp_data_quant = pytplot.data_quants[name]
46 |
47 | if isinstance(temp_data_quant, dict):
48 | # non-record varying variables are stored as dicts
49 | return temp_data_quant['data']
50 |
51 | if xarray:
52 | return temp_data_quant
53 |
54 | if metadata:
55 | return temp_data_quant.attrs
56 |
57 | if 'v1' in temp_data_quant.coords.keys() and 'v2' in temp_data_quant.coords.keys() and 'v3' in temp_data_quant.coords.keys():
58 | variable = namedtuple('variable', ['times', 'y', 'v1', 'v2', 'v3'])
59 | return variable(temp_data_quant.time.values, temp_data_quant.data, temp_data_quant.coords['v1'].values, temp_data_quant.coords['v2'].values, temp_data_quant.coords['v3'].values)
60 | elif 'v1' in temp_data_quant.coords.keys() and 'v2' in temp_data_quant.coords.keys():
61 | variable = namedtuple('variable', ['times', 'y', 'v1', 'v2'])
62 | return variable(temp_data_quant.time.values, temp_data_quant.data, temp_data_quant.coords['v1'].values, temp_data_quant.coords['v2'].values)
63 | elif 'v1' in temp_data_quant.coords.keys():
64 | variable = namedtuple('variable', ['times', 'y', 'v1'])
65 | return variable(temp_data_quant.time.values, temp_data_quant.data, temp_data_quant.coords['v1'].values)
66 | elif 'v' in temp_data_quant.coords.keys():
67 | variable = namedtuple('variable', ['times', 'y', 'v'])
68 | return variable(temp_data_quant.time.values, temp_data_quant.data, temp_data_quant.coords['v'].values)
69 | elif 'spec_bins' in temp_data_quant.coords.keys():
70 | variable = namedtuple('variable', ['times', 'y', 'v'])
71 | return variable(temp_data_quant.time.values, temp_data_quant.data, temp_data_quant.coords['spec_bins'].values)
72 | variable = namedtuple('variable', ['times', 'y'])
73 |
74 | return variable(temp_data_quant.time.values, temp_data_quant.data)
75 |
--------------------------------------------------------------------------------
/pytplot/get_timespan.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | from . import tplot_utilities
8 |
9 |
10 | def get_timespan(name):
11 | """
12 | This function extracts the time span from the Tplot Variables stored in memory.
13 |
14 | Parameters:
15 | name : str
16 | Name of the tplot variable
17 |
18 | Returns:
19 | time_begin : float
20 | The beginning of the time series
21 | time_end : float
22 | The end of the time series
23 |
24 | Examples:
25 | >>> # Retrieve the time span from Variable 1
26 | >>> import pytplot
27 | >>> x_data = [1,2,3,4,5]
28 | >>> y_data = [1,2,3,4,5]
29 | >>> pytplot.store_data("Variable1", data={'x':x_data, 'y':y_data})
30 | >>> time1, time2 = pytplot.get_timespan("Variable1")
31 |
32 | """
33 |
34 | if name not in pytplot.data_quants.keys():
35 | print("That name is currently not in pytplot")
36 | return
37 |
38 | return pytplot.data_quants[name].attrs['plot_options']['trange'][0], pytplot.data_quants[name].attrs['plot_options']['trange'][1]
--------------------------------------------------------------------------------
/pytplot/get_ylimits.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 |
8 |
9 | def get_ylimits(name, trg=None):
10 | """
11 | This function will get extract the y-limits from the Tplot Variables stored in memory.
12 |
13 | Parameters:
14 | name : str
15 | Name of the tplot variable
16 | trg : list, optional
17 | The time range that you would like to look in
18 |
19 | Returns:
20 | ymin : float
21 | The minimum value of y
22 | ymax : float
23 | The maximum value of y
24 |
25 | Examples:
26 | >>> # Retrieve the y-limits from Variable 1
27 | >>> import pytplot
28 | >>> x_data = [1,2,3,4,5]
29 | >>> y_data = [1,2,3,4,5]
30 | >>> pytplot.store_data("Variable1", data={'x':x_data, 'y':y_data})
31 | >>> y1, y2 = pytplot.get_ylimits("Variable1")
32 |
33 | """
34 | if isinstance(name, int):
35 | name = list(pytplot.data_quants.keys())[name-1]
36 | if not isinstance(name, list):
37 | name = [name]
38 | name_num = len(name)
39 | ymin = None
40 | ymax = None
41 |
42 | for i in range(name_num):
43 |
44 | if name[i] not in pytplot.data_quants.keys():
45 | print(str(name[i]) + " is currently not in pytplot.")
46 | return
47 | y = pytplot.data_quants[name[i]]
48 |
49 | # Slice the data around a time range
50 | if trg is not None:
51 | y = y.sel(time=slice(trg[0], trg[1]))
52 |
53 | ymin = y.min(skipna=True)
54 | ymax = y.max(skipna=False)
55 |
56 | return ymin, ymax
57 |
--------------------------------------------------------------------------------
/pytplot/importers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/importers/__init__.py
--------------------------------------------------------------------------------
/pytplot/importers/netcdf_to_tplot.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import xarray as xr
3 | from pytplot import tplot, store_data
4 | import pytplot
5 | import calendar
6 |
7 |
8 | def change_time_to_unix_time(time_var):
9 | from netCDF4 import num2date
10 | # A function that takes a variable with units of 'seconds/minutes/hours/etc. since YYYY-MM-DD:HH:MM:SS/etc
11 | # and converts the variable to seconds since epoch
12 | units = time_var.units
13 | dates = num2date(time_var[:], units=units)
14 | unix_times = list()
15 | for date in dates:
16 | unix_time = calendar.timegm(date.timetuple())
17 | unix_times.append(unix_time)
18 | return unix_times
19 |
20 |
21 | def netcdf_to_tplot(filenames, time ='', prefix='', suffix='', plot=False, merge=False):
22 | '''
23 | This function will automatically create tplot variables from CDF files.
24 |
25 | Parameters:
26 | filenames : str/list of str
27 | The file names and full paths of netCDF files.
28 | time: str
29 | The name of the netCDF file's time variable.
30 | prefix: str
31 | The tplot variable names will be given this prefix. By default,
32 | no prefix is added.
33 | suffix: str
34 | The tplot variable names will be given this suffix. By default,
35 | no suffix is added.
36 | plot: bool
37 | The data is plotted immediately after being generated. All tplot
38 | variables generated from this function will be on the same plot.
39 | By default, a plot is not created.
40 | merge: bool
41 | If True, then data from different netCDF files will be merged into
42 | a single pytplot variable.
43 |
44 | Returns:
45 | List of tplot variables created.
46 |
47 | Examples:
48 | >>> #Create tplot variables from a GOES netCDF file
49 | >>> import pytplot
50 | >>> file = "/Users/user_name/goes_files/g15_epead_a16ew_1m_20171201_20171231.nc"
51 | >>> pytplot.netcdf_to_tplot(file, prefix='mvn_')
52 |
53 | >>> #Add a prefix, and plot immediately.
54 | >>> import pytplot
55 | >>> file = "/Users/user_name/goes_files/g15_epead_a16ew_1m_20171201_20171231.nc"
56 | >>> pytplot.netcdf_to_tplot(file, prefix='goes_prefix_', plot=True)
57 |
58 | '''
59 |
60 | from netCDF4 import Dataset
61 |
62 | stored_variables = []
63 |
64 | if isinstance(filenames, str):
65 | filenames = [filenames]
66 | elif isinstance(filenames, list):
67 | filenames = filenames
68 | else:
69 | print("Invalid filenames input.")
70 | #return stored_variables
71 |
72 | for filename in filenames:
73 |
74 | # Read in file
75 | file = Dataset(filename, "r+")
76 |
77 | # Creating dictionary that will contain variables and their attributes
78 | vars_and_atts = {}
79 | for name, variable in file.variables.items():
80 | vars_and_atts[name] = {}
81 | for attrname in variable.ncattrs():
82 | vars_and_atts[name][attrname] = getattr(variable, attrname)
83 |
84 | # Filling in missing values for each variable with np.nan (if values are not already nan)
85 | # and saving the masked variables to a new dictionary
86 | masked_vars = {} # Dictionary containing properly masked variables
87 | for var in vars_and_atts.keys():
88 | reg_var = file.variables[var]
89 | try:
90 | var_fill_value = vars_and_atts[var]['missing_value']
91 | if np.isnan(var_fill_value) != True:
92 | # We want to force missing values to be nan so that plots don't look strange
93 | var_mask = np.ma.masked_where(reg_var == np.float32(var_fill_value), reg_var)
94 | var_filled = np.ma.filled(var_mask, np.nan)
95 | masked_vars[var] = var_filled
96 | elif np.isnan(var_fill_value) == True:
97 | # missing values are already np.nan, don't need to do anything
98 | var_filled = reg_var
99 | masked_vars[var] = var_filled
100 | except: # continue # Go to next iteration, this variable doesn't have data to mask (probably just a descriptor variable (i.e., 'base_time')
101 | var_filled = reg_var
102 | masked_vars[var] = var_filled
103 |
104 | # Most files are from GOES data, which seems to usually have 'time_tag' in them that contain time information.
105 | # There is an exception filter below that will allow a user to pick a different time variable if time_tag doesn't exist.
106 | if time != '':
107 | time_var = file[time]
108 | unix_times = change_time_to_unix_time(time_var)
109 |
110 | elif time == '':
111 | time = input('Please enter time variable name. \nVariable list: {l}'.format(l=vars_and_atts.keys()))
112 | while True:
113 | if time not in vars_and_atts.keys():
114 | # Making sure we input a valid response (i.e., the variable exists in the dataset), and also avoiding
115 | # plotting a time variable against time.... because I don't even know what that would mean and uncover.
116 | print('Not a valid variable name, please try again.')
117 | continue
118 | elif time in vars_and_atts.keys():
119 | time_var = time
120 | unix_times = change_time_to_unix_time(time_var)
121 |
122 | for i,var in enumerate(file.variables):
123 | # Here, we are making sure that the variables are time-based, otherwise we don't want to store them as tplot variables!
124 | if 'record' in file[var].dimensions[0] or 'time' in file[var].dimensions[0]:
125 | # Store the data now, as well as merge variables if that's desired
126 | var_name = prefix + var + suffix
127 | to_merge = False
128 | if (var_name in pytplot.data_quants.keys() and (merge == True)):
129 | prev_data_quant = pytplot.data_quants[var_name].values
130 | to_merge = True
131 |
132 | tplot_data = {'x': unix_times, 'y': masked_vars[var]}
133 | store_data(var_name, tplot_data)
134 | if var_name not in stored_variables:
135 | stored_variables.append(var_name)
136 |
137 | if to_merge == True:
138 | cur_data_quant = pytplot.data_quants[var_name].values
139 | merged_data = [prev_data_quant, cur_data_quant]
140 | pytplot.data_quants[var_name].values = xr.concat(merged_data).values
141 |
142 | # If we are interested in seeing a quick plot of the variables, do it
143 | if plot:
144 | tplot(stored_variables)
145 | else:
146 | # If the variable isn't time-bound, we're going to look at the next variable
147 | continue
148 |
149 | return stored_variables
150 |
151 |
--------------------------------------------------------------------------------
/pytplot/link.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | import numpy as np
8 |
9 | def link(names, link_name, link_type='alt'):
10 | '''
11 | Simply adds metadata to the tplot variables, specifying which other tplot variables
12 | contain other coordinates, typically positional information.
13 |
14 | Parameters:
15 | names: str or list of str
16 | The names to link
17 | link_name: str
18 | The tplot variable to link to the names
19 | link_type: str
20 | The relationship that link_name has to the names. Values can be
21 | lat, lon, alt, x, y, z, mso_x, mso_y, mso_z
22 | '''
23 |
24 | link_type = link_type.lower()
25 | if not isinstance(names, list):
26 | names = [names]
27 |
28 | for i in names:
29 | if i not in pytplot.data_quants.keys():
30 | print(str(i) + " is not currently in pytplot.")
31 | return
32 |
33 | if isinstance(i,int):
34 | i = list(pytplot.data_quants.keys())[i-1]
35 |
36 | pytplot.data_quants[pytplot.data_quants[i].name].attrs['plot_options']['links'][link_type] = link_name
37 |
38 | return
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/pytplot/replace_data.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | import numpy as np
8 | from pytplot import tplot_utilities as utilities
9 | from copy import deepcopy
10 | from collections import OrderedDict
11 |
12 |
13 | def replace_data(tplot_name, new_data):
14 | """
15 | This function will replace all of the data in a tplot variable
16 |
17 | Parameters:
18 | tplot_name : str
19 | The name of the tplot variable
20 | new_data : np array (or something that can be converted to a np.array)
21 | The data to replace the
22 |
23 | Returns:
24 | None
25 |
26 | Examples:
27 | >>> # Copy Variable 1 into a new Variable 2
28 | >>> import pytplot
29 | >>> pytplot.replace_data("Variable1", [[1,2,3,4],[5,6,7,8]])
30 |
31 | """
32 |
33 | # if old name input is a number, convert to corresponding name
34 | if isinstance(tplot_name, int):
35 | tplot_name = pytplot.data_quants[tplot_name].name
36 |
37 | # check if old name is in current dictionary
38 | if tplot_name not in pytplot.data_quants.keys():
39 | print(f"{tplot_name} is currently not in pytplot")
40 | return
41 |
42 | new_data_np = np.asarray(new_data)
43 | shape_old = pytplot.data_quants[tplot_name].values.shape
44 | shape_new = new_data_np.shape
45 | if shape_old != shape_new:
46 | print(f"Dimensions do not match for replace data. {shape_new} does not equal {shape_old}. Returning...")
47 | return
48 |
49 | pytplot.data_quants[tplot_name].values = new_data_np
50 |
51 | pytplot.data_quants[tplot_name].attrs['plot_options']['yaxis_opt']['y_range'] = utilities.get_y_range(pytplot.data_quants[tplot_name])
52 |
53 | return
54 |
--------------------------------------------------------------------------------
/pytplot/sampledata/test_data.tplot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/pytplot/sampledata/test_data.tplot
--------------------------------------------------------------------------------
/pytplot/timebar.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from . import tplot_utilities
7 | from bokeh.models import Span
8 | import pytplot
9 |
10 | def timebar(t, varname = None, databar = False, delete = False, color = 'black', thick = 1, dash = False):
11 | """
12 | This function will add a vertical bar to all time series plots. This is useful if you
13 | want to bring attention to a specific time.
14 |
15 | Parameters:
16 | t : flt/list
17 | The time in seconds since Jan 01 1970 to place the vertical bar. If a list of numbers are supplied,
18 | multiple bars will be created. If "databar" is set, then "t" becomes the point on the y axis to
19 | place a horizontal bar.
20 | varname : str/list, optional
21 | The variable(s) to add the vertical bar to. If not set, the default is to add it to all current plots.
22 | databar : bool, optional
23 | This will turn the timebar into a horizontal data bar. If this is set True, then variable "t" becomes
24 | the point on the y axis to place a horizontal bar.
25 | delete : bool, optional
26 | If set to True, at lease one varname must be supplied. The timebar at point "t" for variable "varname"
27 | will be removed.
28 | color : str
29 | The color of the bar
30 | thick : int
31 | The thickness of the bar
32 | dash : bool
33 | If set to True, the bar is dashed rather than solid
34 |
35 | Returns:
36 | None
37 |
38 | Examples:
39 | >>> # Place a green time bar at 2017-07-17 00:00:00
40 | >>> import pytplot
41 | >>> pytplot.timebar(1500249600, color='green')
42 |
43 | >>> # Place a dashed data bar at 5500 on the y axis
44 | >>> pytplot.timebar(5500, dashed=True, databar=True)
45 |
46 | >>> Place 3 magenta time bars of thickness 5
47 | at [2015-12-26 05:20:01, 2015-12-26 08:06:40, 2015-12-26 08:53:19]
48 | for variable 'sgx' plot
49 | >>> pytplot.timebar([1451107201,1451117200,1451119999],'sgx',color='m',thick=5)
50 |
51 | """
52 |
53 | # make sure t entered is a list
54 | if not isinstance(t, list):
55 | t = [t]
56 |
57 | # if entries in list not numerical, run str_to_int
58 | if not isinstance(t[0], (int, float, complex)):
59 | t1 = []
60 | for time in t:
61 | t1.append(tplot_utilities.str_to_int(time))
62 | t = t1
63 |
64 | dim = 'height'
65 | if databar is True:
66 | dim = 'width'
67 |
68 | dash_pattern = 'solid'
69 | if dash is True:
70 | dash_pattern = 'dashed'
71 |
72 |
73 | if delete is True:
74 | tplot_utilities.timebar_delete(t, varname, dim)
75 | return
76 |
77 | #if no varname specified, add timebars to every plot
78 | if varname is None:
79 | num_bars = len(t)
80 | for i in range(num_bars):
81 | tbar = {}
82 | tbar['location'] = t[i]
83 | tbar['dimension'] = dim
84 | tbar['line_color'] = pytplot.tplot_utilities.rgb_color(color)[0]
85 | tbar['line_width'] = thick
86 | tbar['line_dash'] = dash_pattern
87 | for name in pytplot.data_quants:
88 | temp_data_quants = pytplot.data_quants[name]
89 | if isinstance(temp_data_quants, dict):
90 | # non-record varying variable
91 | continue
92 | temp_data_quants.attrs['plot_options']['time_bar'].append(tbar)
93 | #if varname specified
94 | else:
95 | if not isinstance(varname, list):
96 | varname = [varname]
97 | for j in varname:
98 | if j not in pytplot.data_quants.keys():
99 | print(str(j) + "is currently not in pytplot")
100 | else:
101 | num_bars = len(t)
102 | for i in range(num_bars):
103 | tbar = {}
104 | tbar['location'] = t[i]
105 | tbar['dimension'] = dim
106 | tbar['line_color'] = pytplot.tplot_utilities.rgb_color(color)[0]
107 | tbar['line_width'] = thick
108 | tbar['line_dash'] = dash_pattern
109 | temp_data_quants = pytplot.data_quants[j]
110 | if isinstance(temp_data_quants, dict):
111 | # non-record varying variable
112 | continue
113 | temp_data_quants.attrs['plot_options']['time_bar'].append(tbar)
114 | return
--------------------------------------------------------------------------------
/pytplot/timespan.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from . import tplot_utilities
7 | from .xlim import xlim
8 |
9 | def timespan(t1, dt, keyword = 'days'):
10 | """
11 | This function will set the time range for all time series plots. This is a wrapper for the function "xlim" to
12 | better handle time axes.
13 |
14 | Parameters:
15 | t1 : flt/str
16 | The time to start all time series plots. Can be given in seconds since epoch, or as a string
17 | in the format "YYYY-MM-DD HH:MM:SS"
18 | dt : flt
19 | The time duration of the plots. Default is number of days.
20 | keyword : str
21 | Sets the units of the "dt" variable. Days, hours, minutes, and seconds are all accepted.
22 |
23 | Returns:
24 | None
25 |
26 | Examples:
27 | >>> # Set the timespan to be 2017-07-17 00:00:00 plus 1 day
28 | >>> import pytplot
29 | >>> pytplot.timespan(1500249600, 1)
30 |
31 | >>> # The same as above, but using different inputs
32 | >>> pytplot.timespan("2017-07-17 00:00:00", 24, keyword='hours')
33 |
34 | """
35 |
36 | if keyword is 'days':
37 | dt *= 86400
38 | elif keyword is 'hours':
39 | dt *= 3600
40 | elif keyword is 'minutes':
41 | dt *= 60
42 | elif keyword is 'seconds':
43 | dt *= 1
44 | else:
45 | print("Invalid 'keyword' option.\nEnum(None, 'hours', 'minutes', 'seconds', 'days')")
46 |
47 | if not isinstance(t1, (int, float, complex)):
48 | t1 = tplot_utilities.str_to_int(t1)
49 | t2 = t1+dt
50 | xlim(t1, t2)
51 |
52 | return
--------------------------------------------------------------------------------
/pytplot/timestamp.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | import datetime
8 |
9 | def timestamp(val):
10 | """
11 | This function will turn on a time stamp that shows up at the bottom of every generated plot.
12 |
13 | Parameters
14 | val str
15 | A string that can either be 'on' or 'off'.
16 |
17 | Returns
18 | None
19 |
20 | Examples
21 | # Turn on the timestamp
22 | import pytplot
23 | pytplot.timestamp('on')
24 | """
25 |
26 |
27 |
28 | if val is 'on':
29 | todaystring = datetime.datetime.now().strftime('%Y-%m-%d %H%M%S')
30 | pytplot.extra_layouts['time_stamp'] = todaystring
31 | else:
32 | if 'time_stamp' in pytplot.extra_layouts:
33 | del pytplot.extra_layouts['time_stamp']
34 |
35 | return
--------------------------------------------------------------------------------
/pytplot/tlimit.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | from __future__ import division
7 | import pytplot
8 |
9 | from .xlim import xlim
10 |
11 | def tlimit(arg):
12 |
13 | if arg is 'last':
14 | xlast = pytplot.lim_info['xlast']
15 | pytplot.lim_info['xlast'] = pytplot.tplot_opt_glob['x_range']
16 | pytplot.tplot_opt_glob['x_range'] = xlast
17 | elif arg is 'full':
18 | pytplot.tplot_opt_glob['x_range'] = pytplot.lim_info['xfull']
19 | elif isinstance(arg, list):
20 | minn = arg[0]
21 | maxx = arg[1]
22 | xlim(minn, maxx)
23 |
24 | return
--------------------------------------------------------------------------------
/pytplot/tplot_copy.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | from copy import deepcopy
8 | from collections import OrderedDict
9 |
10 |
11 | def tplot_copy(old_name, new_name):
12 | """
13 | This function will copy a tplot variables that is already stored in memory.
14 |
15 | Parameters:
16 | name : str
17 | Old name of the Tplot Variable
18 | new_name : str
19 | Name of the copied Tplot Variable
20 |
21 | Returns:
22 | None
23 |
24 | Examples:
25 | >>> # Copy Variable 1 into a new Variable 2
26 | >>> import pytplot
27 | >>> pytplot.tplot_copy("Variable1", "Variable2")
28 |
29 | """
30 |
31 | # if old name input is a number, convert to corresponding name
32 | if isinstance(old_name, int):
33 | if isinstance(pytplot.data_quants[old_name], dict):
34 | old_name = pytplot.data_quants[old_name]['name']
35 | else:
36 | old_name = pytplot.data_quants[old_name].name
37 |
38 | # check if old name is in current dictionary
39 | if old_name not in pytplot.data_quants.keys():
40 | print("That name is currently not in pytplot")
41 | return
42 |
43 | # Add a new data quantity with the copied data
44 | if isinstance(pytplot.data_quants[old_name], dict):
45 | # old variable is a non-record varying variable
46 | pytplot.store_data(new_name, data={'y': pytplot.data_quants[old_name]['data']})
47 | else:
48 | attr_dict = deepcopy(pytplot.data_quants[old_name].attrs)
49 | data_dict = {}
50 | data_dict['x'] = pytplot.data_quants[old_name].coords['time'].values
51 | data_dict['y'] = pytplot.data_quants[old_name].values
52 | if len(data_dict['y'].shape) <= 2:
53 | pass
54 | else:
55 | for c in pytplot.data_quants[old_name].coords:
56 | if c == 'time' or c == 'spec_bins':
57 | continue
58 | data_dict[c] = pytplot.data_quants[old_name].coords[c].values
59 | pytplot.store_data(new_name, data=data_dict)
60 | pytplot.data_quants[new_name].attrs = attr_dict
61 |
62 | return
63 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | from .add_across import add_across
7 | from .add import add
8 | from .avg_res_data import avg_res_data
9 | from .clip import clip
10 | from .crop import crop
11 | from .deflag import deflag
12 | from .degap import degap
13 | from .derive import derive
14 | from .divide import divide
15 | from .flatten import flatten
16 | from .interp_nan import interp_nan
17 | from .tinterp import tinterp
18 | from .join_vec import join_vec
19 | from .multiply import multiply
20 | from .resample import resample
21 | from .spec_mult import spec_mult
22 | from .split_vec import split_vec
23 | from .subtract import subtract
24 | from .pwr_spec import pwr_spec
--------------------------------------------------------------------------------
/pytplot/tplot_math/add.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import copy
8 |
9 | def add(tvar1,tvar2,new_tvar=None):
10 | """
11 | Adds two tplot variables together. Will interpolate if the two are not on the same time cadence.
12 |
13 | Parameters:
14 | tvar1 : str
15 | Name of first tplot variable.
16 | tvar2 : int/float
17 | Name of second tplot variable
18 | new_tvar : str
19 | Name of new tvar for added data. If not set, then the data in tvar1 is replaced.
20 |
21 | Returns:
22 | None
23 |
24 | Examples:
25 | >>> pytplot.store_data('a', data={'x':[0,4,8,12,16], 'y':[1,2,3,4,5]})
26 | >>> pytplot.store_data('c', data={'x':[0,4,8,12,16,19,21], 'y':[1,4,1,7,1,9,1]})
27 | >>> pytplot.add('a','c','a+c')
28 | """
29 |
30 | # interpolate tvars
31 | tv2 = pytplot.tplot_math.tinterp(tvar1, tvar2)
32 |
33 | # separate and subtract data
34 | data1 = pytplot.data_quants[tvar1].values
35 | data2 = pytplot.data_quants[tv2].values
36 | data = data1 + data2
37 |
38 | # store subtracted data
39 | if new_tvar is None:
40 | pytplot.data_quants[tvar1].values = data
41 | return tvar1
42 |
43 | if 'spec_bins' in pytplot.data_quants[tvar1].coords:
44 | pytplot.store_data(new_tvar, data={'x': pytplot.data_quants[tvar1].coords['time'].values, 'y': data,
45 | 'v': pytplot.data_quants[tvar1].coords['spec_bins'].values})
46 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
47 | else:
48 | pytplot.store_data(new_tvar, data={'x': pytplot.data_quants[tvar1].coords['time'].values, 'y': data})
49 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
50 |
51 | return new_tvar
--------------------------------------------------------------------------------
/pytplot/tplot_math/add_across.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import numpy as np
8 | import copy
9 |
10 | def add_across(tvar,column_range=None,new_tvar=None):
11 | """
12 | Adds across columns in the tplot variable
13 |
14 | .. note::
15 | This analysis routine assumes the data is no more than 2 dimensions. If there are more, they may become flattened!
16 |
17 | Parameters:
18 | tvar : str
19 | Name of tplot variable.
20 | column_range: list of ints
21 | The columns to add together. For example, if [1,4] is given here, columns 1, 2, 3, and 4 will be added together.
22 | If not set, then every column is added.
23 | new_tvar : str
24 | Name of new tvar for averaged data. If not set, then the variable is replaced
25 |
26 | Returns:
27 | None
28 |
29 | Examples:
30 | >>> #Add across every column in the data
31 | >>> pytplot.store_data('d', data={'x':[2,5,8,11,14,17,21], 'y':[[1,1,50],[2,2,3],[100,4,47],[4,90,5],[5,5,99],[6,6,25],[7,7,-5]]})
32 | >>> pytplot.add_across('d',new_tvar='d_aa')
33 | >>> print(pytplot.data_quants['d_aa'].data)
34 |
35 | >>> #Add across specific columns in the data
36 | >>> pytplot.store_data('b', data={'x':[2,5,8,11,14,17,20], 'y':[[1,1,1,1,1,1],[2,2,5,4,1,1],[100,100,3,50,1,1],[4,4,8,58,1,1],[5,5,9,21,1,1],[6,6,2,2,1,1],[7,7,1,6,1,1]]})
37 | >>> pytplot.add_across('b',column_range=[[1,2],[3,4]],new_tvar='b_aap')
38 | >>> print(pytplot.data_quants['b_aap'].data)
39 | """
40 | # separate and add data
41 |
42 | if 'spec_bins' in pytplot.data_quants[tvar].coords:
43 | d, s = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvar)
44 | else:
45 | d = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvar, no_spec_bins=True)
46 | s = None
47 |
48 | time = d.index.copy()
49 | data1 = d.copy()
50 | if s is not None:
51 | data2 = s.copy()
52 | else:
53 | data2=None
54 | data = []
55 | spec_data = []
56 |
57 | if column_range is None:
58 | column_range = [0, len(d.columns)-1]
59 |
60 | #grab column data
61 | if len(column_range)==2 and isinstance(column_range[0],int):
62 | range_start = column_range[0]
63 | range_end = column_range[1]
64 | add_col = list(range(range_start,range_end+1))
65 | datasum = data1[add_col].sum(axis=1)
66 | data = data + [list(datasum)]
67 | else:
68 | for i in column_range:
69 | #if not a list
70 | if type(i) == int:
71 | data = data + [list(data1[i])]
72 | #sum across listed column range
73 | else:
74 | range_start = i[0]
75 | range_end = i[1]
76 | add_col = list(range(range_start,range_end+1))
77 | datasum = data1[add_col].sum(axis=1)
78 | data = data + [list(datasum)]
79 |
80 | if data2 is not None:
81 | if len(column_range) == 2 and isinstance(column_range[0], int):
82 | range_start = column_range[0]
83 | range_end = column_range[1]
84 | add_col = list(range(range_start, range_end + 1))
85 | datasum = data2[add_col].mean(axis=1)
86 | spec_data = spec_data + [list(datasum)]
87 | else:
88 | for i in column_range:
89 | # if not a list
90 | if type(i) == int:
91 | spec_data = spec_data + [list(data2[i])]
92 | # sum across listed column range
93 | else:
94 | range_start = i[0]
95 | range_end = i[1]
96 | add_col = list(range(range_start, range_end + 1))
97 | datasum = data2[add_col].mean(axis=1)
98 | spec_data = spec_data + [list(datasum)]
99 |
100 | #store added data
101 | if s is None:
102 | pytplot.store_data(new_tvar,data={'x':time, 'y':np.transpose(data)})
103 | else:
104 | pytplot.store_data(new_tvar, data={'x': time, 'y':np.transpose(data), 'v': np.transpose(spec_data)})
105 |
106 | #pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
107 |
108 | return
109 |
110 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/avg_res_data.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 | import numpy as np
3 | import copy
4 |
5 | def avg_res_data(tvar,res,new_tvar=None):
6 | """
7 | Averages the variable over a specified period of time.
8 |
9 | Parameters:
10 | tvar1 : str
11 | Name of tplot variable.
12 | res : int/float
13 | The new data resolution
14 | new_tvar : str
15 | Name of new tvar for averaged data. If not set, then the data in tvar is replaced.
16 |
17 | Returns:
18 | None
19 |
20 | Examples:
21 | >>> #Average the data over every two seconds
22 | >>> pytplot.store_data('d', data={'x':[2,5,8,11,14,17,21], 'y':[[1,1,50],[2,2,3],[100,4,47],[4,90,5],[5,5,99],[6,6,25],[7,7,-5]]})
23 | >>> pytplot.avg_res_data('d',2,'d2res')
24 | >>> print(pytplot.data_quants['d'].values)
25 | """
26 |
27 | tvar_new = pytplot.data_quants[tvar].coarsen(time=res, boundary='trim').mean()
28 | tvar_new.name = pytplot.data_quants[tvar].name
29 | tvar_new.attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
30 |
31 | if new_tvar is None:
32 | pytplot.data_quants[tvar] = tvar_new
33 | else:
34 | if 'spec_bins' in pytplot.data_quants[tvar].coords:
35 | pytplot.store_data(new_tvar, data={'x': tvar_new.coords['time'].values, 'y': tvar_new.values,
36 | 'v': tvar_new.coords['spec_bins'].values})
37 | else:
38 | pytplot.store_data(new_tvar, data={'x': tvar_new.coords['time'].values, 'y': tvar_new.values})
39 |
40 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
41 |
42 | return
43 |
44 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/clip.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import numpy as np
8 | import copy
9 |
10 | def clip(tvar,ymin,ymax,new_tvar=None):
11 | """
12 | Change out-of-bounds data to NaN.
13 |
14 | Parameters:
15 | tvar1 : str
16 | Name of tvar to use for data clipping.
17 | ymin : int/float
18 | Minimum value to keep (inclusive)
19 | ymax : int/float
20 | Maximum value to keep (inclusive)
21 | newtvar : str
22 | Name of new tvar for clipped data storage. If not specified, tvar will be replaced
23 |
24 | Returns:
25 | None
26 |
27 | Examples:
28 | >>> Make any values below 2 and above 6 equal to NaN.
29 | >>> pytplot.store_data('d', data={'x':[2,5,8,11,14,17,21], 'y':[[1,1],[2,2],[100,100],[4,4],[5,5],[6,6],[7,7]]})
30 | >>> pytplot.clip('d',2,6,'e')
31 | """
32 |
33 | a = copy.deepcopy(pytplot.data_quants[tvar].where(pytplot.data_quants[tvar] >= ymin))
34 | a = copy.deepcopy(a.where(a <= ymax))
35 |
36 | if new_tvar is None:
37 | a.name = tvar
38 | pytplot.data_quants[tvar] = a
39 | else:
40 | if 'spec_bins' in a.coords:
41 | pytplot.store_data(new_tvar, data={'x': a.coords['time'], 'y': a.values, 'v': a.coords['spec_bins']})
42 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
43 | else:
44 | pytplot.store_data(new_tvar, data={'x': a.coords['time'], 'y': a.values})
45 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
46 |
47 | return
48 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/crop.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import copy
8 |
9 | def crop(tvar1,tvar2, replace=True):
10 | """
11 | Crops both tplot variable so that their times are the same. This is done automatically by other processing routines if times do not match up.
12 |
13 | Parameters:
14 | tvar1 : str
15 | Name of the first tplot variable
16 | tvar2 : str
17 | Name of the second tplot variable
18 | replace : bool, optional
19 | If true, the data in the original tplot variables are replaced. Otherwise, new variables are created.
20 |
21 | Returns:
22 | None
23 |
24 |
25 | Examples:
26 | >>> pytplot.store_data('a', data={'x':[0,4,8,12,16], 'y':[1,2,3,4,5]})
27 | >>> pytplot.store_data('b', data={'x':[2,5,8,11,14,17,20], 'y':[[1,1,1,1,1,1],[2,2,5,4,1,1],[100,100,3,50,1,1],[4,4,8,58,1,1],[5,5,9,21,1,1],[6,6,2,2,1,1],[7,7,1,6,1,1]]})
28 | >>> pytplot.crop('a','b')
29 | """
30 |
31 | # grab time and data arrays
32 | tv1 = pytplot.data_quants[tvar1].copy()
33 | tv2 = pytplot.data_quants[tvar2].copy()
34 | # find first and last time indices
35 | t0_1 = tv1.coords['time'][0]
36 | t0_2 = tv2.coords['time'][0]
37 | tx_1 = tv1.coords['time'][-1]
38 | tx_2 = tv2.coords['time'][-1]
39 | # find cut locations
40 | cut1 = max([t0_1, t0_2])
41 | cut2 = min([tx_1, tx_2])
42 | # trim data
43 | tv1 = tv1.sel(time=slice(cut1, cut2))
44 | tv1.attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
45 | tv2 = tv2.sel(time=slice(cut1, cut2))
46 | tv2.attrs = copy.deepcopy(pytplot.data_quants[tvar2].attrs)
47 | # Replace the variables if specified
48 | if replace:
49 | pytplot.data_quants[tvar1] = tv1
50 | pytplot.data_quants[tvar1].name = tvar1
51 | pytplot.data_quants[tvar2] = tv2
52 | pytplot.data_quants[tvar2].name = tvar2
53 | return
54 | else:
55 | pytplot.data_quants[tvar1 + '_cropped'] = copy.deepcopy(tv1)
56 | pytplot.data_quants[tvar1 + '_cropped'].attrs = copy.deepcopy(tv1.attrs)
57 | pytplot.data_quants[tvar1 + '_cropped'].name = tvar1+ '_cropped'
58 | pytplot.data_quants[tvar2 + '_cropped'] = copy.deepcopy(tv2)
59 | pytplot.data_quants[tvar2 + '_cropped'].attrs = copy.deepcopy(tv2.attrs)
60 | pytplot.data_quants[tvar2 + '_cropped'].name = tvar2 + '_cropped'
61 | return tvar2 + '_cropped'
--------------------------------------------------------------------------------
/pytplot/tplot_math/deflag.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import copy
8 |
9 |
10 | def deflag(tvar,flag,new_tvar=None):
11 | """
12 | Change specified 'flagged' data to NaN.
13 |
14 | Parameters:
15 | tvar1 : str
16 | Name of tplot variable to use for data clipping.
17 | flag : int,list
18 | Flagged data will be converted to NaNs.
19 | newtvar : str
20 | Name of new tvar for deflagged data storage. If not specified, then the data in tvar1 will be replaced.
21 |
22 | Returns:
23 | None
24 |
25 | Examples:
26 | >>> # Remove any instances of [100,90,7,2,57] from 'd', store in 'e'.
27 | >>> pytplot.store_data('d', data={'x':[2,5,8,11,14,17,21], 'y':[[1,1],[2,2],[100,4],[4,90],[5,5],[6,6],[7,7]]})
28 | >>> pytplot.deflag('d',[100,90,7,2,57],'e')
29 | """
30 |
31 | a = copy.deepcopy(pytplot.data_quants[tvar].where(pytplot.data_quants[tvar]!=flag))
32 |
33 |
34 | if new_tvar is None:
35 | a.name = tvar
36 | pytplot.data_quants[tvar] = a
37 | else:
38 | if 'spec_bins' in a.coords:
39 | pytplot.store_data(new_tvar, data={'x': a.coords['time'], 'y': a.values, 'v': a.coords['spec_bins']})
40 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
41 | else:
42 | pytplot.store_data(new_tvar, data={'x': a.coords['time'], 'y': a.values})
43 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
44 |
45 | return
46 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/degap.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import numpy as np
8 | import pandas as pd
9 | import copy
10 |
11 | def degap(tvar,dt,margin,func='nan',new_tvar = None):
12 | '''
13 | Fills gaps in the data either with NaNs or the last number.
14 |
15 | Parameters:
16 | tvar : str
17 | Name of tplot variable to modify
18 | dt : int/float
19 | Step size of the data in seconds
20 | margin : int/float, optional
21 | The maximum deviation from the step size allowed before degapping occurs. In other words, if you'd like to fill in data every 4 seconds
22 | but occasionally the data is 4.1 seconds apart, set the margin to .1 so that a data point is not inserted there.
23 | func : str, optional
24 | Either 'nan' or 'ffill', which overrides normal interpolation with NaN
25 | substitution or forward-filled values.
26 | new_tvar : str, optional
27 | The new tplot variable name to store the data into. If None, then the data is overwritten.
28 |
29 | Returns:
30 | None
31 |
32 | Examples:
33 | >>> # TODO
34 | '''
35 |
36 | gap_size = np.diff(pytplot.data_quants[tvar].coords['time'])
37 | gap_index_locations = np.where(gap_size > dt+margin)
38 | new_tvar_index = pytplot.data_quants[tvar].coords['time']
39 | values_to_add = np.array([])
40 | for i in gap_index_locations[0]:
41 | values_to_add = np.append(values_to_add, np.arange(new_tvar_index[i], new_tvar_index[i+1], dt))
42 |
43 | new_index = np.sort(np.unique(np.concatenate((values_to_add, new_tvar_index))))
44 |
45 | if func == 'nan':
46 | method = None
47 | if func == 'ffill':
48 | method = 'ffill'
49 |
50 | a = pytplot.data_quants[tvar].reindex({'time': new_index}, method=method)
51 |
52 | if new_tvar is None:
53 | a.name = tvar
54 | a.attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
55 | pytplot.data_quants[tvar] = copy.deepcopy(a)
56 | else:
57 | if 'spec_bins' in a.coords:
58 | pytplot.store_data(new_tvar, data={'x': a.coords['time'], 'y': a.values, 'v': a.coords['spec_bins']})
59 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
60 | else:
61 | pytplot.store_data(new_tvar, data={'x': a.coords['time'], 'y': a.values})
62 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
63 |
64 | return
65 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/derive.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import numpy as np
8 | import copy
9 |
10 | def derive(tvar,new_tvar=None):
11 | """
12 | Takes the derivative of the tplot variable.
13 |
14 | Parameters:
15 | tvar : str
16 | Name of tplot variable.
17 | new_tvar : str
18 | Name of new tplot variable. If not set, then the data in tvar is replaced.
19 |
20 | Returns:
21 | None
22 |
23 | Examples:
24 | >>> pytplot.store_data('b', data={'x':[2,5,8,11,14,17,20], 'y':[[1,1,1,1,1,1],[2,2,5,4,1,1],[100,100,3,50,1,1],[4,4,8,58,1,1],[5,5,9,21,1,1],[6,6,2,2,1,1],[7,7,1,6,1,1]]})
25 | >>> pytplot.derive('b','dbdt')
26 | >>> print(pytplot.data_quants['dbdt'].values)
27 | """
28 |
29 | a = pytplot.data_quants[tvar].differentiate('time')
30 | if new_tvar is None:
31 | a.name = tvar
32 | a.attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
33 | pytplot.data_quants[tvar] = a
34 | else:
35 | data = {'x':a.coords['time'], 'y':a.values}
36 | for coord in a.coords:
37 | if coord != 'time' and coord != 'spec_bins':
38 | data[coord] = a.coords[coord].values
39 |
40 | pytplot.store_data(new_tvar, data=data)
41 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
42 |
43 | return
44 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/divide.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import numpy as np
8 | import copy
9 |
10 | def divide(tvar1,tvar2,new_tvar=None):
11 | """
12 | Divides two tplot variables. Will interpolate if the two are not on the same time cadence.
13 |
14 | Parameters:
15 | tvar1 : str
16 | Name of first tplot variable.
17 | tvar2 : int/float
18 | Name of second tplot variable
19 | new_tvar : str
20 | Name of new tvar for divided data. If not set, then the data in tvar1 is replaced.
21 |
22 | Returns:
23 | None
24 |
25 | Examples:
26 | >>> pytplot.store_data('a', data={'x':[0,4,8,12,16], 'y':[1,2,3,4,5]})
27 | >>> pytplot.store_data('c', data={'x':[0,4,8,12,16,19,21], 'y':[1,4,1,7,1,9,1]})
28 | >>> pytplot.divide('a','c','a_over_c')
29 | """
30 | # interpolate tvars
31 | tv2 = pytplot.tplot_math.tinterp(tvar1, tvar2)
32 | # separate and divide data
33 | data1 = pytplot.data_quants[tvar1].values
34 | data2 = pytplot.data_quants[tv2].values
35 | data = data1 / data2
36 | # store divided data
37 | if new_tvar is None:
38 | pytplot.data_quants[tvar1].values = data
39 | return tvar1
40 | if 'spec_bins' in pytplot.data_quants[tvar1].coords:
41 | pytplot.store_data(new_tvar, data={'x': pytplot.data_quants[tvar1].coords['time'].values, 'y': data,
42 | 'v': pytplot.data_quants[tvar1].coords['spec_bins'].values})
43 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
44 | else:
45 | pytplot.store_data(new_tvar, data={'x': pytplot.data_quants[tvar1].coords['time'].values, 'y': data})
46 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
47 |
48 | return new_tvar
49 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/examples.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import numpy as np
8 |
9 | pytplot.store_data('a', data={'x':[0,4,8,12,16], 'y':[1,2,3,4,5]})
10 | pytplot.store_data('b', data={'x':[2,5,8,11,14,17,20], 'y':[[1,1,1,1,1,1],[2,2,5,4,1,1],[100,100,3,50,1,1],[4,4,8,58,1,1],[5,5,9,21,1,1],[6,6,2,2,1,1],[7,7,1,6,1,1]]})
11 | pytplot.store_data('c', data={'x':[0,4,8,12,16,19,21], 'y':[1,4,1,7,1,9,1]})
12 | pytplot.store_data('d', data={'x':[2,5,8,11,14,17,21], 'y':[[1,1,50],[2,2,3],[100,4,47],[4,90,5],[5,5,99],[6,6,25],[7,7,-5]]})
13 | pytplot.store_data('e', data={'x':[2,5,8,11,14,17,21], 'y':[[np.nan,1,1],[np.nan,2,3],[4,np.nan,47],[4,np.nan,5],[5,5,99],[6,6,25],[7,np.nan,-5]]})
14 | pytplot.store_data('g', data={'x':[0,4,8,12,16,19,21], 'y':[[8,1,1],[100,2,3],[4,2,47],[4,39,5],[5,5,99],[6,6,25],[7,-2,-5]]})
15 | pytplot.store_data('h', data={'x':[0,4,8,12,16,19,21], 'y':[[8,1,1],[100,2,3],[4,2,47],[4,39,5],[5,5,99],[6,6,25],[7,-2,-5]],'v':[[1,1,50],[2,2,3],[100,4,47],[4,90,5],[5,5,99],[6,6,25],[7,7,-5]]})
16 |
17 | print('add_across_partial')
18 | pytplot.add_across_partial('b',[[1,2],[3,4]],'b_aap')
19 | print(pytplot.data_quants['b_aap'].data)
20 |
21 | print('add_across')
22 | pytplot.add_across('d','d_aa')
23 | print(pytplot.data_quants['d_aa'].data)
24 |
25 | print('add')
26 | pytplot.add('a','c','a+c')
27 | print(pytplot.data_quants['a+c'].data)
28 |
29 | print('avg_res_data')
30 | pytplot.avg_res_data('d',2,'d2res')
31 | print(pytplot.data_quants['d'].data)
32 | print(pytplot.data_quants['d2res'].data)
33 | pytplot.avg_res_data('h', 7, 'h7res')
34 | print(pytplot.data_quants['h'].data)
35 | print(pytplot.data_quants['h'].spec_bins)
36 | print(pytplot.data_quants['h7res'].data)
37 | print(pytplot.data_quants['h7res'].spec_bins)
38 |
39 | print('clip')
40 | pytplot.clip('d',2,6,'d_clip')
41 | print(pytplot.data_quants['d_clip'].data)
42 | pytplot.clip('h',2,6,'h_clip')
43 | print(pytplot.data_quants['h_clip'].data)
44 | print(pytplot.data_quants['h_clip'].spec_bins)
45 |
46 | print('crop')
47 | pytplot.crop('a','b')
48 |
49 | print('deflag')
50 | pytplot.deflag('d',[100,90,7,2,57],'d_deflag')
51 | print(pytplot.data_quants['d'].data)
52 | print(pytplot.data_quants['d_deflag'].data)
53 | pytplot.deflag('h',[100,90,7,2,57],'h_deflag')
54 | print(pytplot.data_quants['h_deflag'].data)
55 | print(pytplot.data_quants['h_deflag'].spec_bins)
56 |
57 |
58 | print('derive')
59 | pytplot.derive('b','dbdt')
60 | print(pytplot.data_quants['dbdt'].data)
61 | pytplot.derive('h','dhdt')
62 | print(pytplot.data_quants['dhdt'].data)
63 | print(pytplot.data_quants['dhdt'].spec_bins)
64 |
65 | print('divide')
66 | pytplot.divide('a','b','a/b')
67 | print(pytplot.data_quants['a/b'].data)
68 |
69 | print('flatten_full')
70 | pytplot.flatten_full('d','d_ff')
71 | print(pytplot.data_quants['d_ff'].data)
72 |
73 | print('flatten')
74 | pytplot.flatten('d',8,14,'d_flatten')
75 | print(pytplot.data_quants['d_flatten'].data)
76 |
77 | print('interp_nan')
78 | pytplot.interp_nan('e','e_nonan',s_limit=5)
79 | print(pytplot.data_quants['e_nonan'].data)
80 |
81 | print('interpolate')
82 | pytplot.tinterp('a','c',interp='cubic')
83 | print(pytplot.data_quants['c_interp'].data)
84 |
85 | print('join_vec')
86 | pytplot.join_vec(['d','e','g'],'deg')
87 | print(pytplot.data_quants['deg'].data)
88 |
89 | print('multiply')
90 | pytplot.multiply('a','c','ac',interp='linear')
91 | print(pytplot.data_quants['ac'].data)
92 |
93 |
94 | print('resample')
95 | # pytplot.resample('d',[3,4,5,6,7,18],'d_resampled')
96 | # print(pytplot.data_quants['d_resampled'].data)
97 | pytplot.resample('h',[3,4,5,6,7,18],'h_resampled')
98 | print(pytplot.data_quants['h_resampled'].data)
99 | print(pytplot.data_quants['h_resampled'].spec_bins)
100 |
101 | print('spec_mult')
102 | pytplot.spec_mult('h','h_specmult')
103 | print(pytplot.data_quants['h_specmult'].data)
104 |
105 | print('split_vec')
106 | pytplot.split_vec('b',['b1','b2','b3'],[0,[1,3],4])
107 | print(pytplot.data_quants['b1'].data)
108 | print(pytplot.data_quants['b2'].data)
109 | print(pytplot.data_quants['b3'].data)
110 | defaultlist = pytplot.split_vec('b')
111 | print(pytplot.data_quants['b'].data)
112 | print(pytplot.data_quants['data_3'].data)
113 | print(defaultlist)
114 |
115 | print('subtract')
116 | pytplot.subtract('a','c','a-c')
117 | print(pytplot.data_quants['a-c'].data)
--------------------------------------------------------------------------------
/pytplot/tplot_math/flatten.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import copy
8 |
9 | def flatten(tvar,range=None,new_tvar=None):
10 | """
11 | Divides the column by an average over specified time
12 |
13 | .. note::
14 | This analysis routine assumes the data is no more than 2 dimensions. If there are more, they may become flattened!
15 |
16 | Parameters:
17 | tvar : str
18 | Name of first tplot variable.
19 | range : [int, int], optional
20 | The time range to average over. The default is the whole range.
21 | new_tvar : str
22 | Name of new tvar for added data. If not set, then a name is made up.
23 |
24 | Returns:
25 | None
26 |
27 | Examples:
28 | >>> # Divide each column by the average of the data between times 8 and 14
29 | >>> pytplot.store_data('d', data={'x':[2,5,8,11,14,17,21], 'y':[[1,1,50],[2,2,3],[100,4,47],[4,90,5],[5,5,99],[6,6,25],[7,7,-5]]})
30 | >>> pytplot.flatten('d',[8,14],'d_flatten')
31 | >>> print(pytplot.data_quants['d_flatten'].values)
32 | """
33 |
34 |
35 | if new_tvar is None:
36 | new_tvar = tvar + "_flattened"
37 |
38 | if 'spec_bins' in pytplot.data_quants[tvar].coords:
39 | df, s = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvar)
40 | else:
41 | df = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvar, no_spec_bins=True)
42 | s=None
43 |
44 | if range is None:
45 | pass
46 |
47 | time = df.index
48 | #if time given not an index, choose closest time
49 | if range is None:
50 | df_index = list(df.columns)
51 | # divide by column average
52 | for i in df_index:
53 | df[i] = df[i] / df[i].mean()
54 | else:
55 | if range[0] not in time:
56 | tdiff = abs(time - range[0])
57 | start_t = time[tdiff.argmin()]
58 | if range[1] not in time:
59 | tdiff = abs(time - range[1])
60 | end_t = time[tdiff.argmin()]
61 | df_index = list(df.columns)
62 |
63 | #divide by specified time average
64 | for i in df_index:
65 | df[i] = df[i]/((df.loc[start_t:end_t])[i]).mean()
66 |
67 | if s is not None:
68 | pytplot.store_data(new_tvar,data = {'x':df.index,'y':df.values, 'v': s.values})
69 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
70 | else:
71 | pytplot.store_data(new_tvar, data={'x': df.index, 'y': df.values})
72 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
73 | return
--------------------------------------------------------------------------------
/pytplot/tplot_math/interp_nan.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import copy
8 |
9 | def interp_nan(tvar, new_tvar=None, s_limit=None):
10 | """
11 | Interpolates the tplot variable through NaNs in the data. This is basically just a wrapper for xarray's interpolate_na function.
12 |
13 | .. note::
14 | This analysis routine assumes the data is no more than 2 dimensions. If there are more, they may become flattened!
15 |
16 | Parameters:
17 | tvar : str
18 | Name of tplot variable.
19 | s_limit : int or float, optional
20 | The maximum size of the gap in seconds to not interpolate over. I.e. if there are too many NaNs in a row, leave them there.
21 | new_tvar : str
22 | Name of new tvar for added data. If not set, then the original tvar is replaced.
23 |
24 | Returns:
25 | None
26 |
27 | Examples:
28 | >>> # Interpolate through the np.NaN values
29 | >>> pytplot.store_data('e', data={'x':[2,5,8,11,14,17,21], 'y':[[np.nan,1,1],[np.nan,2,3],[4,np.nan,47],[4,np.nan,5],[5,5,99],[6,6,25],[7,np.nan,-5]]})
30 | >>> pytplot.interp_nan('e','e_nonan',s_limit=5)
31 | >>> print(pytplot.data_quants['e_nonan'].values)
32 | """
33 |
34 | x = pytplot.data_quants[tvar].interpolate_na(dim='time', limit=s_limit)
35 | x.attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
36 |
37 | if new_tvar is None:
38 | pytplot.data_quants[tvar] = x
39 | x.name = tvar
40 | else:
41 | pytplot.data_quants[new_tvar] = x
42 | pytplot.data_quants[new_tvar].name = new_tvar
43 |
44 |
45 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/join_vec.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import pandas as pd
8 | import copy
9 | import xarray as xr
10 |
11 | #JOIN TVARS
12 | #join TVars into single TVar with multiple columns
13 | def join_vec(tvars,new_tvar=None, merge=False):
14 | """
15 | Joins 1D tplot variables into one tplot variable.
16 |
17 | .. note::
18 | This analysis routine assumes the data is no more than 2 dimensions. If there are more, they may become flattened!
19 |
20 | Parameters:
21 | tvars : list of str
22 | Name of tplot variables to join together
23 | new_tvar : str, optional
24 | The name of the new tplot variable. If not specified, a name will be assigned.
25 | merge : bool, optional
26 | Whether or not to merge the created variable into an older variable
27 |
28 | Returns:
29 | None
30 |
31 | Examples:
32 | >>> pytplot.store_data('d', data={'x':[2,5,8,11,14,17,21], 'y':[[1,1,50],[2,2,3],[100,4,47],[4,90,5],[5,5,99],[6,6,25],[7,7,-5]]})
33 | >>> pytplot.store_data('e', data={'x':[2,5,8,11,14,17,21], 'y':[[np.nan,1,1],[np.nan,2,3],[4,np.nan,47],[4,np.nan,5],[5,5,99],[6,6,25],[7,np.nan,-5]]})
34 | >>> pytplot.store_data('g', data={'x':[0,4,8,12,16,19,21], 'y':[[8,1,1],[100,2,3],[4,2,47],[4,39,5],[5,5,99],[6,6,25],[7,-2,-5]]})
35 | >>> pytplot.join_vec(['d','e','g'],'deg')
36 | >>> print(pytplot.data_quants['deg'].values)
37 | """
38 |
39 | if not isinstance(tvars, list):
40 | tvars = [tvars]
41 | if new_tvar is None:
42 | new_tvar = '-'.join(tvars)+'_joined'
43 |
44 | to_merge=False
45 | if new_tvar in pytplot.data_quants.keys() and merge:
46 | prev_data_quant = pytplot.data_quants[new_tvar]
47 | to_merge = True
48 |
49 | for i,val in enumerate(tvars):
50 | if i == 0:
51 | if 'spec_bins' in pytplot.data_quants[tvars[i]].coords:
52 | df, s = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvars[i])
53 | else:
54 | df = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvars[i], no_spec_bins=True)
55 | s = None
56 | else:
57 | if 'spec_bins' in pytplot.data_quants[tvars[i]].coords:
58 | d = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvars[i], no_spec_bins=True)
59 | else:
60 | d = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvars[i], no_spec_bins=True)
61 | df = pd.concat([df,d],axis=1)
62 |
63 | if s is None:
64 | pytplot.store_data(new_tvar,data={'x': df.index,'y': df.values})
65 | else:
66 | pytplot.store_data(new_tvar, data={'x': df.index, 'y': df.values, 'v': s.values})
67 |
68 | if to_merge is True:
69 | cur_data_quant = pytplot.data_quants[new_tvar]
70 | plot_options = copy.deepcopy(pytplot.data_quants[new_tvar].attrs)
71 | pytplot.data_quants[new_tvar] = xr.concat([prev_data_quant, cur_data_quant], dim='time').sortby('time')
72 | pytplot.data_quants[new_tvar].attrs = plot_options
73 |
74 | return new_tvar
75 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/multiply.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import copy
8 |
9 | def multiply(tvar1,tvar2,new_tvar=None):
10 | """
11 | Multiplies two tplot variables. Will interpolate if the two are not on the same time cadence.
12 |
13 | Parameters:
14 | tvar1 : str
15 | Name of first tplot variable.
16 | tvar2 : int/float
17 | Name of second tplot variable
18 | new_tvar : str
19 | Name of new tplot variable. If not set, then the data in tvar1 is replaced.
20 |
21 | Returns:
22 | None
23 |
24 | Examples:
25 | >>> pytplot.store_data('a', data={'x':[0,4,8,12,16], 'y':[1,2,3,4,5]})
26 | >>> pytplot.store_data('c', data={'x':[0,4,8,12,16,19,21], 'y':[1,4,1,7,1,9,1]})
27 | >>> pytplot.multiply('a','c','a_x_c')
28 | """
29 |
30 |
31 | # interpolate tvars
32 | tv2 = pytplot.tplot_math.tinterp(tvar1, tvar2)
33 | # separate and multiply data
34 | data1 = pytplot.data_quants[tvar1].values
35 | data2 = pytplot.data_quants[tv2].values
36 | data = data1 * data2
37 |
38 | if new_tvar is None:
39 | pytplot.data_quants[tvar1].values = data
40 | return tvar1
41 |
42 | if 'spec_bins' in pytplot.data_quants[tvar1].coords:
43 | pytplot.store_data(new_tvar, data={'x': pytplot.data_quants[tvar1].coords['time'].values, 'y': data, 'v':pytplot.data_quants[tvar1].coords['spec_bins'].values})
44 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
45 | else:
46 | pytplot.store_data(new_tvar,data={'x': pytplot.data_quants[tvar1].coords['time'].values, 'y': data})
47 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
48 | return new_tvar
49 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/pwr_spec.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pytplot
3 | from scipy import signal
4 |
5 |
6 | # First pass at the power spectrum function. This is still missing several features of the IDL power spectrum routine, such as
7 | # bin, nohanning, notperhertz, and tm_sensativity. The IDL routine is located in dpwrspc.pro.
8 |
9 | # There is also the issue of this not quite having the same units as the plot I use as my reference.
10 | # https://agupubs.onlinelibrary.wiley.com/doi/full/10.1002/2015GL065366#grl53372-bib-0016
11 | # Interestingly enough, the output is the same if units of seconds are used in the periodogram instead of Hertz.
12 | # Perhaps they calculated it differently?
13 |
14 | def pwr_spec(tvar, nbp=256, nsp=128, name=None):
15 | """
16 | Calculates the power spectrum of a line, and adds a tplot variable for this new spectrogram
17 |
18 | Parameters:
19 | tvar : str
20 | Name of tvar to use
21 | nbp : int, optional
22 | The number of points to use when calculating the FFT
23 | nsp : int, optional
24 | The number of points to shift over to calculate the next FFT
25 | name : str, optional
26 | The name of the new tplot variable created,
27 |
28 | Returns:
29 | None
30 |
31 | Examples:
32 | >>> pytplot.cdf_to_tplot("/path/to/pytplot/testfiles/mvn_euv_l2_bands_20170619_v09_r03.cdf")
33 | >>> pytplot.tplot_math.split_vec('data')
34 | >>> pytplot.pwr_spec('data_0')
35 | >>> pytplot.tplot('data_0_pwrspec')
36 | """
37 |
38 | x = pytplot.data_quants[tvar].coords['time']
39 | y = pytplot.data_quants[tvar].values.squeeze()
40 |
41 | if len(y.shape) > 1:
42 | print("Can only perform action for a single line")
43 |
44 | l = len(x)
45 | x_new = []
46 | f_new = []
47 | pxx_new = []
48 | shift_lsp = np.arange(0, l-1, nsp)
49 | for i in shift_lsp:
50 |
51 | x_n = x[i:i+nbp]
52 | y_n = y[i:i+nbp]
53 | if len(x_n) < nbp:
54 | continue
55 |
56 | median_diff_between_points = np.median(np.diff(x_n))
57 |
58 | w = signal.get_window("hanning", nbp)
59 | f,pxx = signal.periodogram(y_n, fs=(1/median_diff_between_points), window=w, detrend='linear')
60 | f = f[1:-1]
61 | pxx = pxx[1:-1]
62 | x_new.append((x_n[-1] + x_n[0]) / 2)
63 | f_new.append(f)
64 | pxx_new.append(pxx)
65 |
66 | if name is None:
67 | name = tvar + "_pwrspec"
68 |
69 | pytplot.store_data(name, data={'x': x_new, 'y': pxx_new, 'v': f_new})
70 | pytplot.options(name, 'spec', 1)
71 | pytplot.options(name, 'zlog', 1)
72 | pytplot.options(name, 'ylog', 1)
73 |
74 | return
75 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/resample.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import numpy as np
8 | from scipy import interpolate
9 | from scipy.interpolate import interp1d
10 | import copy
11 |
12 | def resample(tvar,times,new_tvar=None):
13 | """
14 | Linearly interpolates data to user-specified values. To interpolate one tplot variable to another, use tinterp.
15 |
16 | .. note::
17 | This analysis routine assumes the data is no more than 2 dimensions. If there are more, they may become flattened!
18 |
19 | Parameters:
20 | tvar : str
21 | Name of tvar whose data will be interpolated to specified times.
22 | times : int/list
23 | Desired times for interpolation.
24 | new_tvar : str
25 | Name of new tvar in which to store interpolated data. If none is specified, tvar will be overwritten
26 |
27 | Returns:
28 | None
29 |
30 | Examples:
31 | >>> # Interpolate data for 'd' to values [3,4,5,6,7,18].
32 | >>> pytplot.store_data('d', data={'x':[2,5,8,11,14,17,21], 'y':[[1,1],[2,2],[100,100],[4,4],[5,5],[6,6],[7,7]]})
33 | >>> pytplot.tplot_resample('d',[3,4,5,6,7,18],'d_resampled')
34 | """
35 |
36 | x = pytplot.data_quants[tvar].interp(time=times)
37 |
38 | if new_tvar is None:
39 | x.attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
40 | pytplot.data_quants[tvar] = x
41 | x.name = tvar
42 | else:
43 | pytplot.data_quants[new_tvar] = copy.deepcopy(x)
44 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
45 | pytplot.data_quants[new_tvar].name = new_tvar
46 |
47 | return
48 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/spec_mult.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 | import pandas as pd
3 | import copy
4 |
5 | def spec_mult(tvar,new_tvar=None):
6 | """
7 | Multiplies the data by the stored spectrogram bins and created a new tplot variable
8 |
9 | .. note::
10 | This analysis routine assumes the data is no more than 2 dimensions. If there are more, they may become flattened!
11 |
12 | Parameters:
13 | tvar : str
14 | Name of tplot variable
15 | times : int/list
16 | Desired times for interpolation.
17 | new_tvar : str
18 | Name of new tvar in which to store interpolated data. If none is specified, a name will be created.
19 |
20 | Returns:
21 | None
22 |
23 | Examples:
24 | >>> pytplot.store_data('h', data={'x':[0,4,8,12,16,19,21], 'y':[[8,1,1],[100,2,3],[4,2,47],[4,39,5],[5,5,99],[6,6,25],[7,-2,-5]],'v':[[1,1,50],[2,2,3],[100,4,47],[4,90,5],[5,5,99],[6,6,25],[7,7,-5]]})
25 | >>> pytplot.spec_mult('h','h_specmult')
26 | >>> print(pytplot.data_quants['h_specmult'].data)
27 | """
28 |
29 | if new_tvar is None:
30 | new_tvar = tvar+'_specmult'
31 | if 'spec_bins' not in pytplot.data_quants[tvar].coords:
32 | print("Specified variable must have spec bins stored. Returning...")
33 | return
34 | d, s = pytplot.tplot_utilities.convert_tplotxarray_to_pandas_dataframe(tvar)
35 | dataframe = d.values
36 | specframe = s.values
37 | new_df = pd.DataFrame(dataframe*specframe, columns=d.columns, index=d.index)
38 | pytplot.store_data(new_tvar,data={'x': new_df.index,'y': new_df.values})
39 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar].attrs)
40 | return
41 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/split_vec.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 | import numpy as np
3 |
4 | def split_vec(tvar, new_name=None, columns='all', suffix=None):
5 | """
6 | Splits up 2D data into many 1D tplot variables.
7 |
8 | .. note::
9 | This analysis routine assumes the data is no more than 2 dimensions. If there are more, they may become flattened!
10 |
11 | Parameters:
12 | tvar : str
13 | Name of tplot variable to split up
14 | newtvars : int/list, optional
15 | The names of the new tplot variables. This must be the same length as the number of variables created.
16 | columns : list of ints, optional
17 | The specific column numbers to grab from the data. The default is to split all columns.
18 |
19 | Returns:
20 | None
21 |
22 | Examples:
23 | >>> pytplot.store_data('b', data={'x':[2,5,8,11,14,17,20], 'y':[[1,1,1,1,1,1],[2,2,5,4,1,1],[100,100,3,50,1,1],[4,4,8,58,1,1],[5,5,9,21,1,1],[6,6,2,2,1,1],[7,7,1,6,1,1]]})
24 | >>> pytplot.tplot_math.split_vec('b',['b1','b2','b3'],[0,[1,3],4])
25 | >>> print(pytplot.data_quants['b2'].values)
26 | """
27 |
28 | # Make sure the tvar is found
29 | if tvar not in pytplot.data_quants:
30 | print(f"Error: {tvar} not found in memory.")
31 | return
32 |
33 | # Give a default to the new name
34 | if new_name is None:
35 | new_name = tvar
36 |
37 | # Gather data from the tvar
38 | alldata = pytplot.get_data(tvar)
39 | time = alldata[0]
40 | data = alldata[1]
41 | dim = data.shape
42 |
43 | # If already size one, simply return
44 | if len(dim) == 1:
45 | return [tvar]
46 |
47 | vec_length = dim[1]
48 |
49 | # Determine what the suffix list will be
50 | if suffix is not None:
51 | if vec_length > len(suffix):
52 | print(f"split_vec error: number of columns ({vec_length}) is greater than the number of suffix entered")
53 | else:
54 | if vec_length == 3:
55 | suffix = ["_x", "_y", "_z"]
56 | else:
57 | suffix = []
58 | for i in range(vec_length):
59 | suffix.append("_"+str(i))
60 |
61 |
62 | created_variables = []
63 |
64 | #grab column data
65 | if columns == 'all':
66 | columns = range(vec_length)
67 |
68 | for i in columns:
69 |
70 | #if not a list
71 | if isinstance(i,list):
72 | range_start = i[0]
73 | range_end = i[1]
74 | else:
75 | range_start = i
76 | range_end = i
77 | split_col = list(range(range_start,range_end+1))
78 | split_name = new_name + suffix[i]
79 | created_variables = created_variables + [split_name]
80 |
81 | data_for_tplot = {'x':time, 'y':data[:,split_col].squeeze()}
82 |
83 | if not pytplot.store_data(split_name,data=data_for_tplot):
84 | raise Exception(f"Failed to store {split_name} in pytplot.")
85 |
86 |
87 | return created_variables
88 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/subtract.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import copy
8 |
9 | def subtract(tvar1,tvar2,new_tvar=None):
10 | """
11 | Subtracts two tplot variables. Will interpolate if the two are not on the same time cadence.
12 |
13 | Parameters:
14 | tvar1 : str
15 | Name of first tplot variable.
16 | tvar2 : int/float
17 | Name of second tplot variable
18 | new_tvar : str
19 | Name of new tvar for added data. If not set, then the data in tvar1 is replaced.
20 |
21 | Returns:
22 | None
23 |
24 | Examples:
25 | >>> pytplot.store_data('a', data={'x':[0,4,8,12,16], 'y':[1,2,3,4,5]})
26 | >>> pytplot.store_data('c', data={'x':[0,4,8,12,16,19,21], 'y':[1,4,1,7,1,9,1]})
27 | >>> pytplot.subtract('a','c','a-c')
28 | """
29 |
30 | #interpolate tvars
31 | tv2 = pytplot.tplot_math.tinterp(tvar1,tvar2)
32 |
33 | #separate and subtract data
34 | data1 = pytplot.data_quants[tvar1].values
35 | data2 = pytplot.data_quants[tv2].values
36 | data = data1 - data2
37 |
38 | #store subtracted data
39 | if new_tvar is None:
40 | pytplot.data_quants[tvar1].values = data
41 | return tvar1
42 |
43 | if 'spec_bins' in pytplot.data_quants[tvar1].coords:
44 | pytplot.store_data(new_tvar, data={'x': pytplot.data_quants[tvar1].coords['time'].values, 'y': data, 'v':pytplot.data_quants[tvar1].coords['spec_bins'].values})
45 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
46 | else:
47 | pytplot.store_data(new_tvar,data={'x': pytplot.data_quants[tvar1].coords['time'].values, 'y': data})
48 | pytplot.data_quants[new_tvar].attrs = copy.deepcopy(pytplot.data_quants[tvar1].attrs)
49 |
50 | return new_tvar
51 |
--------------------------------------------------------------------------------
/pytplot/tplot_math/tinterp.py:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/Pytplot
5 |
6 | import pytplot
7 | import copy
8 |
9 |
10 | def tinterp(tvar1,tvar2,replace=False):
11 | """
12 | Interpolates one tplot variable to another one's time cadence. This is done automatically by other processing routines.
13 |
14 | Parameters:
15 | tvar1 : str
16 | Name of first tplot variable whose times will be used to interpolate tvar2's data.
17 | tvar2 : str
18 | Name of second tplot variable whose data will be interpolated.
19 | replace : bool, optional
20 | If true, the data in the original tplot variable is replaced. Otherwise, a variable is created.
21 |
22 | Returns:
23 | new_var2, the name of the new tplot variable
24 |
25 | Examples:
26 | >>> pytplot.store_data('a', data={'x':[0,4,8,12,16], 'y':[1,2,3,4,5]})
27 | >>> pytplot.store_data('c', data={'x':[0,4,8,12,16,19,21], 'y':[1,4,1,7,1,9,1]})
28 | >>> pytplot.tinterp('a','c')
29 | >>> print(pytplot.data_quants['c_interp'].data)
30 | """
31 | new_tvar2 = pytplot.data_quants[tvar2].interp_like(pytplot.data_quants[tvar1])
32 |
33 | if replace:
34 | pytplot.data_quants[tvar2] = new_tvar2
35 | return
36 | else:
37 | pytplot.data_quants[tvar1 + '_tinterp'] = copy.deepcopy(new_tvar2)
38 | pytplot.data_quants[tvar1 + '_tinterp'].attrs = copy.deepcopy(new_tvar2.attrs)
39 | pytplot.data_quants[tvar1 + '_tinterp'].name = tvar1 + '_tinterp'
40 |
41 | return tvar1 + '_tinterp'
42 |
--------------------------------------------------------------------------------
/pytplot/tplot_names.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 |
8 | def tplot_names(quiet=False):
9 | """
10 | This function will print out and return a list of all current Tplot Variables stored in the memory.
11 |
12 | Parameters:
13 | quiet : bool
14 | If True, does not print out the variables (only returns the list variables)
15 |
16 | Returns:
17 | list : list of str
18 | A list of all Tplot Variables stored in the memory
19 |
20 | Examples:
21 | >>> import pytplot
22 | >>> x_data = [1,2,3,4,5]
23 | >>> y_data = [1,2,3,4,5]
24 | >>> pytplot.store_data("Variable1", data={'x':x_data, 'y':y_data})
25 | >>> tnames = pyplot.tplot_names()
26 | 0 : Variable 1
27 |
28 | """
29 |
30 | index = 0
31 | return_names=[]
32 |
33 | # TODO: Print out links as well?
34 |
35 | for key, _ in pytplot.data_quants.items():
36 | if isinstance(pytplot.data_quants[key], dict):
37 | # non-record varying variables are stored as dictionaries
38 | if isinstance(key, str):
39 | names_to_print = key
40 |
41 | if quiet != True:
42 | print(index, ":", names_to_print)
43 |
44 | return_names.append(names_to_print)
45 | index += 1
46 | continue
47 |
48 | if len(pytplot.data_quants[key].attrs['plot_options']['overplots']) != 0:
49 | names_to_print = pytplot.data_quants[key].name + " data from: "
50 | for oplot_name in pytplot.data_quants[key].attrs['plot_options']['overplots']:
51 | names_to_print = names_to_print + " " + oplot_name
52 |
53 | else:
54 | if isinstance(key, str):
55 | names_to_print = pytplot.data_quants[key].name
56 |
57 | if quiet != True:
58 | print(index, ":", names_to_print)
59 |
60 | index += 1
61 |
62 | return_names.append(names_to_print)
63 | return return_names
--------------------------------------------------------------------------------
/pytplot/tplot_options.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | from . import tplot_utilities
8 |
9 | def tplot_options(option, value):
10 | """
11 | This function allows the user to set GLOBAL options for the generated plots.
12 |
13 | Parameters:
14 | option : str
15 | The name of the option. See section below
16 | value : str/int/float/list
17 | The value of the option. See section below.
18 |
19 | Options:
20 | ================ ========== =====
21 | Options Value type Notes
22 | ================ ========== =====
23 | title str Title of the the entire output
24 | title_size int Font size of the output
25 | wsize [int, int] [height, width], pixel size of the plot window
26 | title_align int Offset position in pixels of the title
27 | var_label srt Name of the tplot variable to be used as another x axis
28 | alt_range [flt, flt] The min and max altitude to be plotted on all alt plots
29 | map_x_range [int, int] The min and max longitude to be plotted on all map plots
30 | map_y_range [int, int] The min and max latitude to be plotted on all map plots
31 | x_range [flt, flt] The min and max x_range (usually time) to be plotted on all Spec/1D plots
32 | data_gap int Number of seconds with consecutive nan values allowed before no interp should occur
33 | roi [str, str] Times between which there's a region of interest for a user
34 | crosshair bool Option allowing crosshairs and crosshair legend
35 | vertical_spacing int The space in pixels between two plots
36 | show_all_axes bool Whether or not to just use one axis at the bottom of the plot
37 | black_background bool Whether or not to make plot backgrounds black w/ white text
38 | axis_font_size int The font size of the axis ticks. Default is 10.
39 | axis_tick_num [tuples] A list of tuples that determines how many ticks appear. See pyqtgraph textFillLimits
40 | yaxis_width int The number of pixels wide of the y axis
41 | y_axis_zoom bool Set True if the mouse wheel should zoom in on the y axis as well as the x on plots.
42 | ================ ========== =====
43 |
44 | Returns:
45 | None
46 |
47 | Examples:
48 | >>> # Set the plot title
49 | >>> import pytplot
50 | >>> pytplot.tplot_options('title', 'SWEA Data for Orbit 1563')
51 |
52 | >>> # Set the window size
53 | >>> pytplot.tplot_options('wsize', [1000,500])
54 |
55 |
56 | """
57 |
58 | option = option.lower()
59 |
60 | temp = tplot_utilities.set_tplot_options(option, value, pytplot.tplot_opt_glob)
61 | pytplot.tplot_opt_glob = temp
62 |
63 | return
64 |
--------------------------------------------------------------------------------
/pytplot/tplot_rename.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | from collections import OrderedDict
8 |
9 | def tplot_rename(old_name, new_name):
10 | """
11 | This function will rename tplot variables that are already stored in memory.
12 |
13 | Parameters:
14 | old_name : str
15 | Old name of the Tplot Variable
16 | new_name : str
17 | New name of the Tplot Variable
18 |
19 | Returns:
20 | None
21 |
22 | Examples:
23 | >>> # Rename Variable 1 to Variable 2
24 | >>> import pytplot
25 | >>> pytplot.tplot_rename("Variable1", "Variable2")
26 |
27 | """
28 |
29 | #if old name input is a number, convert to corresponding name
30 | if isinstance(old_name, int):
31 | if isinstance(pytplot.data_quants[old_name], dict):
32 | old_name = pytplot.data_quants[old_name]['name']
33 | else:
34 | old_name = pytplot.data_quants[old_name].name
35 |
36 | # check if old name is in current dictionary
37 | if old_name not in pytplot.data_quants.keys():
38 | print("That name is currently not in pytplot")
39 | return
40 |
41 | #remake dictionary with new name in old name's slot
42 | d = pytplot.data_quants
43 | d2 = OrderedDict([(new_name, v) if k == old_name else (k, v) for k, v in d.items()])
44 | new_data_quants = d2
45 | for key in d2:
46 | if isinstance(new_data_quants[key], dict):
47 | # the variable is non-record varying
48 | new_data_quants[key]['name'] = key
49 | else:
50 | new_data_quants[key].name = key
51 |
52 | pytplot.data_quants = new_data_quants
53 | return
54 |
--------------------------------------------------------------------------------
/pytplot/xlim.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 | from . import tplot_utilities
8 | from bokeh.models import Range1d
9 |
10 | def xlim(min, max):
11 | """
12 | This function will set the x axis range for all time series plots
13 |
14 | Parameters:
15 | min : flt
16 | The time to start all time series plots. Can be given in seconds since epoch, or as a string
17 | in the format "YYYY-MM-DD HH:MM:SS"
18 | max : flt
19 | The time to end all time series plots. Can be given in seconds since epoch, or as a string
20 | in the format "YYYY-MM-DD HH:MM:SS"
21 |
22 | Returns:
23 | None
24 |
25 | Examples:
26 | >>> # Set the timespan to be 2017-07-17 00:00:00 plus 1 day
27 | >>> import pytplot
28 | >>> pytplot.xlim(1500249600, 1500249600 + 86400)
29 |
30 | >>> # The same as above, but using different inputs
31 | >>> pytplot.xlim("2017-07-17 00:00:00", "2017-07-18 00:00:00")
32 |
33 | """
34 | if not isinstance(min, (int, float, complex)):
35 | min = tplot_utilities.str_to_int(min)
36 | if not isinstance(max, (int, float, complex)):
37 | max = tplot_utilities.str_to_int(max)
38 | if 'x_range' in pytplot.tplot_opt_glob:
39 | pytplot.lim_info['xlast'] = pytplot.tplot_opt_glob['x_range']
40 | else:
41 | pytplot.lim_info['xfull'] = Range1d(min, max)
42 | pytplot.lim_info['xlast'] = Range1d(min, max)
43 | pytplot.tplot_opt_glob['x_range'] = [min, max]
44 | return
45 |
--------------------------------------------------------------------------------
/pytplot/ylim.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 |
8 | def ylim(name, min, max):
9 | """
10 | This function will set the y axis range displayed for a specific tplot variable.
11 |
12 | Parameters:
13 | name : str
14 | The name of the tplot variable that you wish to set y limits for.
15 | min : flt
16 | The start of the y axis.
17 | max : flt
18 | The end of the y axis.
19 |
20 | Returns:
21 | None
22 |
23 | Examples:
24 | >>> # Change the y range of Variable1
25 | >>> import pytplot
26 | >>> x_data = [1,2,3,4,5]
27 | >>> y_data = [1,2,3,4,5]
28 | >>> pytplot.store_data("Variable1", data={'x':x_data, 'y':y_data})
29 | >>> pytplot.ylim('Variable1', 2, 4)
30 |
31 | """
32 | if name not in pytplot.data_quants.keys():
33 | print("That name is currently not in pytplot.")
34 | return
35 |
36 | pytplot.data_quants[name].attrs['plot_options']['yaxis_opt']['y_range'] = [min, max]
37 |
38 | return
--------------------------------------------------------------------------------
/pytplot/zlim.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Regents of the University of Colorado. All Rights Reserved.
2 | # Released under the MIT license.
3 | # This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
4 | # Verify current version before use at: https://github.com/MAVENSDC/PyTplot
5 |
6 | import pytplot
7 |
8 | def zlim(name, min, max):
9 | """
10 | This function will set the z axis range displayed for a specific tplot variable.
11 | This is only used for spec plots, where the z axis represents the magnitude of the values
12 | in each bin.
13 |
14 | Parameters:
15 | name : str
16 | The name of the tplot variable that you wish to set z limits for.
17 | min : flt
18 | The start of the z axis.
19 | max : flt
20 | The end of the z axis.
21 |
22 | Returns:
23 | None
24 |
25 | Examples:
26 | >>> # Change the z range of Variable1
27 | >>> import pytplot
28 | >>> x_data = [1,2,3]
29 | >>> y_data = [ [1,2,3] , [4,5,6], [7,8,9] ]
30 | >>> v_data = [1,2,3]
31 | >>> pytplot.store_data("Variable3", data={'x':x_data, 'y':y_data, 'v':v_data})
32 | >>> pytplot.zlim('Variable1', 2, 3)
33 |
34 | """
35 | if name not in pytplot.data_quants.keys():
36 | print("That name is currently not in pytplot.")
37 | return
38 |
39 | pytplot.data_quants[name].attrs['plot_options']['zaxis_opt']['z_range'] = [min, max]
40 |
41 | return
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy >= 1.20.0
2 | bokeh >= 1.1,<3.0
3 | pyqtgraph >= 0.11.1
4 | pandas
5 | matplotlib
6 | scipy
7 | cdflib
8 | xarray
9 | pyqt5 >= 5.15.2
10 | pyqtwebengine >= 5.15.2
11 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = pytplot
3 | version = 1.7.28
4 | author = MAVEN SDC
5 | author_email = mavensdc@lasp.colorado.edu
6 | description = A python version of IDL tplot libraries
7 | url = https://github.com/MAVENSDC/pytplot
8 | keywords =
9 | tplot
10 | maven
11 | lasp
12 | IDL
13 | spedas
14 | classifiers =
15 | Development Status :: 5 - Production/Stable
16 | Environment :: Console
17 | Intended Audience :: Science/Research
18 | Operating System :: OS Independent
19 | Programming Language :: Python :: 3.7
20 | Topic :: Utilities
21 | license_file = LICENSE
22 | long_description = file: README.md
23 | long_description_content_type = text/markdown
24 |
25 | [options]
26 | python_requires = >= 3.6
27 | setup_requires =
28 | setuptools >= 38.6
29 | pip >= 10
30 | include_package_data = True
31 | packages = find:
32 | install_requires =
33 | numpy >= 1.20.0
34 | bokeh >= 1.1
35 | pyqtgraph >= 0.11.1
36 | pandas
37 | matplotlib
38 | scipy
39 | cdflib
40 | xarray
41 | pyqt5 >= 5.15.2
42 | pyqtwebengine >= 5.15.2
43 |
44 | [options.extras_require]
45 | tests =
46 | pytest
47 | pytest-cov
48 | coveralls
49 | flake8
50 | mypy
51 | astropy
52 |
53 | [options.entry_points]
54 | console_scripts =
55 |
56 | [flake8]
57 | max-line-length = 132
58 | exclude = .git,__pycache__,.eggs/,doc/,docs/,build/,dist/,archive/
59 |
60 | [coverage:run]
61 | cover_pylib = false
62 | omit =
63 | /home/travis/virtualenv/*
64 | */site-packages/*
65 | */bin/*
66 |
67 | [coverage:report]
68 | exclude_lines =
69 | pragma: no cover
70 | def __repr__
71 | except RuntimeError
72 | except NotImplementedError
73 | except ImportError
74 | except FileNotFoundError
75 | except CalledProcessError
76 | logging.warning
77 | logging.error
78 | logging.critical
79 | if __name__ == .__main__.:
80 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #Uploading new versions to pypi:
2 | #
3 | #python setup.py sdist
4 | #twine upload dist/*
5 | #Username MAVENSDC
6 |
7 |
8 | #################################
9 | #Uploading new versions to conda:
10 | ##################################
11 |
12 | #Note: You'll need to make sure cdflib is up to date on conda as well
13 |
14 | #Run: conda skeleton pypi pytplot
15 | #This should generate a meta.yaml file
16 | #Edit the YAML file so that the requirements/about/extra look like this:
17 |
18 |
19 | '''
20 | requirements:
21 | build:
22 | - setuptools
23 | - twine
24 | - python
25 | - pip
26 | run:
27 | - python
28 | - numpy
29 | - bokeh
30 | - pandas
31 | - matplotlib
32 | - scipy
33 | - xarray
34 | - pyqtgraph
35 | - cdflib
36 |
37 | about:
38 | home: "https://github.com/MAVENSDC/pytplot"
39 | license: "MIT"
40 | summary: "Pytplot is an effort to replicate the functionality IDL tplot library in python"
41 | doc_url: "https://github.com/MAVENSDC/pytplot"
42 | dev_url: "https://github.com/MAVENSDC/pyptlot"
43 |
44 | extra:
45 | recipe-maintainers:
46 | - MAVENSDC
47 | '''
48 |
49 |
50 |
51 | #conda-build pytplot
52 | #conda-build --python 3.6 pytplot
53 | #conda-build --python 3.5 pytplot
54 | #This should put stuff in C:/Anaconda/conda-bld
55 | #conda convert -f --platform all /path/to/created/bundles/file.tar.bz2 -o /path/to/place/converted/files
56 | #anaconda upload /path/to/created/or/converted/bundles/file.tar.bz2
57 | #Username MAVENSDC
58 |
59 | from setuptools import setup
60 | setup()
61 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_alt_plot.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 | import os
3 | import numpy as np
4 |
5 | current_directory = os.path.dirname(os.path.realpath(__file__))
6 |
7 | def test_altitude_plot():
8 |
9 | pytplot.netcdf_to_tplot(current_directory + "/testfiles/g15_xrs_2s_20170619_20170619.nc", time='time_tag')
10 | pytplot.store_data('altitude', data={'x': pytplot.data_quants['A_COUNT'].coords['time'].values, 'y': np.arange(0, len(pytplot.data_quants['A_COUNT'].coords['time'].values), step=1)})
11 | pytplot.link('A_COUNT', 'altitude')
12 | pytplot.xlim('2017-06-19 02:00:00', '2017-06-19 04:00:00')
13 | pytplot.ylim("A_COUNT", 17000, 18000)
14 | pytplot.timebar('2017-06-19 03:00:00', "A_COUNT", color=(100, 255, 0), thick=3)
15 | pytplot.timebar('2017-06-19 03:30:00', "A_COUNT", color='g')
16 | pytplot.options("A_COUNT", 'alt', 1)
17 | pytplot.tplot(2, testing=True)
18 | pytplot.tplot(2, testing=True, bokeh=True)
--------------------------------------------------------------------------------
/tests/test_cdf_to_tplot.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 | import os
3 |
4 | current_directory = os.path.dirname(os.path.realpath(__file__))
5 |
6 | def test_cdf_euv_read():
7 |
8 | pytplot.cdf_to_tplot(current_directory + "/testfiles/mvn_euv_l2_bands_20170619_v09_r03.cdf")
9 | pytplot.tplot('data', testing=True)
10 | pytplot.tplot('data', testing=True, bokeh=True)
11 |
12 | def test_cdf_swe_read():
13 | pytplot.cdf_to_tplot(current_directory + "/testfiles/mvn_swe_l2_svyspec_20170619_v04_r04.cdf")
14 | pytplot.options('diff_en_fluxes', 'colormap', 'magma')
15 | pytplot.options('diff_en_fluxes', 'ztitle', 'FLUX')
16 | pytplot.options('diff_en_fluxes', 'ytitle', 'Energy')
17 | pytplot.options("diff_en_fluxes", "spec", 1)
18 | pytplot.options("diff_en_fluxes", "crosshair_y", "banana")
19 | pytplot.options("diff_en_fluxes", "crosshair_z", "tomato")
20 | pytplot.options("diff_en_fluxes", 'panel_size', 1)
21 | pytplot.options('diff_en_fluxes', 'ylog', 1)
22 | pytplot.options('diff_en_fluxes', 'zlog', 1)
23 | pytplot.tplot('diff_en_fluxes', testing=True)
24 | pytplot.tplot('data', testing=True, bokeh=True)
--------------------------------------------------------------------------------
/tests/test_maps.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 | import os
3 | import numpy as np
4 |
5 | current_directory = os.path.dirname(os.path.realpath(__file__))
6 |
7 | def test_map_plot():
8 | pytplot.netcdf_to_tplot(current_directory + "/testfiles/g15_xrs_2s_20170619_20170619.nc", time='time_tag')
9 |
10 | pytplot.store_data('lat', data={'x': pytplot.data_quants['A_COUNT'].coords['time'].values, 'y': np.arange(0, 90, step=(90 / len(pytplot.data_quants['A_COUNT'].coords['time'].values)))})
11 | pytplot.link('A_COUNT', 'lat', link_type='lat')
12 | pytplot.store_data('lon', data={'x': pytplot.data_quants['A_COUNT'].coords['time'].values, 'y': np.arange(0, 360,step=(360 / len(pytplot.data_quants['A_COUNT'].coords['time'].values)))})
13 | pytplot.link('A_COUNT', 'lon', link_type='lon')
14 | pytplot.xlim('2017-06-19 02:00:00', '2017-06-19 04:00:00')
15 | pytplot.ylim("A_COUNT", 17000, 18000)
16 | pytplot.timebar('2017-06-19 03:00:00', "A_COUNT", color=(100, 255, 0), thick=3)
17 | pytplot.timebar('2017-06-19 03:30:00', "A_COUNT", color='g')
18 | pytplot.options("A_COUNT", 'map', 1)
19 | pytplot.tplot(2, testing=True)
20 | pytplot.tplot(2, testing=True, bokeh=True)
--------------------------------------------------------------------------------
/tests/test_netcdf_to_tplot.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 | import os
3 |
4 | current_directory = os.path.dirname(os.path.realpath(__file__))
5 |
6 | def test_goes_read():
7 |
8 | pytplot.netcdf_to_tplot(current_directory + "/testfiles/g15_xrs_2s_20170619_20170619.nc", time='time_tag')
9 | pytplot.xlim('2017-06-19 02:00:00', '2017-06-19 04:00:00')
10 | pytplot.ylim("B_COUNT", 17000, 18000)
11 | pytplot.timebar('2017-06-19 03:00:00', "B_COUNT", color=(100, 255, 0), thick=3)
12 | pytplot.timebar('2017-06-19 03:30:00', "B_COUNT", color='g')
13 | pytplot.options("B_COUNT", 'ylog', 1)
14 | pytplot.store_data("BCOUNTFLUX", data=["B_COUNT", "B_FLUX"])
15 | pytplot.tplot([1, 2, 3, 4, 5, 7], var_label=6, testing=True)
--------------------------------------------------------------------------------
/tests/test_qt_import.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 |
3 | def test_qt_import():
4 | assert pytplot.using_graphics == True
--------------------------------------------------------------------------------
/tests/test_tplot_math.py:
--------------------------------------------------------------------------------
1 | import pytplot
2 | import os
3 |
4 | current_directory = os.path.dirname(os.path.realpath(__file__))
5 |
6 | def test_math():
7 |
8 | pytplot.cdf_to_tplot(os.path.dirname(os.path.realpath(__file__)) + "/testfiles/mvn_euv_l2_bands_20170619_v09_r03.cdf")
9 | pytplot.tplot_names()
10 |
11 | pytplot.tplot_math.split_vec('mvn_euv_calib_bands')
12 |
13 | pytplot.tplot('mvn_euv_calib_bands_x', testing=True)
14 |
15 | pytplot.tplot_math.subtract('mvn_euv_calib_bands_x', 'mvn_euv_calib_bands_y', new_tvar='s')
16 |
17 | pytplot.tplot('s', testing=True)
18 |
19 | pytplot.tplot_math.add('s', 'mvn_euv_calib_bands_x', new_tvar='a')
20 |
21 | pytplot.tplot(['mvn_euv_calib_bands_x', 'a'], testing=True)
22 |
23 | pytplot.tplot_math.subtract('mvn_euv_calib_bands_x', 'mvn_euv_calib_bands_z', new_tvar='m')
24 |
25 | pytplot.tplot('m', testing=True)
26 |
27 | pytplot.tplot_math.divide('m', 'mvn_euv_calib_bands_z', new_tvar='d')
28 |
29 | pytplot.tplot('d', testing=True)
30 |
31 | pytplot.add_across('mvn_euv_calib_bands', new_tvar='data_summed')
32 |
33 | pytplot.tplot('mvn_euv_calib_bands', testing=True)
34 |
35 | pytplot.avg_res_data('data_summed', res=120)
36 |
37 | pytplot.tplot('data_summed', testing=True)
38 |
39 | pytplot.deflag('mvn_euv_calib_bands', 0, new_tvar='deflagged')
40 |
41 | pytplot.tplot('deflagged', testing=True)
42 |
43 | pytplot.flatten('mvn_euv_calib_bands')
44 |
45 | pytplot.tplot('data_flattened', testing=True)
46 |
47 | pytplot.join_vec(['mvn_euv_calib_bands_x', 'mvn_euv_calib_bands_y', 'mvn_euv_calib_bands_z'], new_tvar='data2')
48 |
49 | pytplot.tplot('data2', testing=True)
50 |
51 | pytplot.pwr_spec('mvn_euv_calib_bands_x')
52 |
53 | pytplot.tplot('mvn_euv_calib_bands_x_pwrspec', testing=True)
54 |
55 | pytplot.derive('mvn_euv_calib_bands_x')
56 |
57 | pytplot.store_data("data3", data=['mvn_euv_calib_bands_x', 'mvn_euv_calib_bands_y', 'mvn_euv_calib_bands_z'])
58 |
59 | pytplot.tplot('data3', testing=True)
60 |
61 | pytplot.cdf_to_tplot(os.path.dirname(os.path.realpath(__file__))+ "/testfiles/mvn_swe_l2_svyspec_20170619_v04_r04.cdf")
62 |
63 | pytplot.resample('mvn_euv_calib_bands_y', pytplot.data_quants['diff_en_fluxes'].coords['time'].values, new_tvar='data_3_resampled')
64 |
65 | pytplot.tplot('data_3_resampled', testing=True)
66 | pytplot.options('diff_en_fluxes', 'spec', 1)
67 | pytplot.spec_mult('diff_en_fluxes')
68 |
69 | pytplot.add_across('diff_en_fluxes_specmult', new_tvar='tot_en_flux', column_range=[[0, 10], [10, 20], [20, 30]])
70 |
71 | pytplot.options('diff_en_fluxes', 'ylog', 1)
72 | pytplot.options('diff_en_fluxes', 'zlog', 1)
73 | pytplot.options('tot_en_flux', 'ylog', 1)
74 | pytplot.ylim('tot_en_flux', 1, 100)
75 | pytplot.tplot(['diff_en_fluxes', 'tot_en_flux'], testing=True)
76 |
77 | pytplot.split_vec('tot_en_flux')
78 |
79 | pytplot.add('tot_en_flux_x', 'mvn_euv_calib_bands_y', new_tvar='weird_data')
80 |
81 | pytplot.tplot('weird_data', testing=True)
82 |
--------------------------------------------------------------------------------
/tests/testfiles/g15_xrs_2s_20170619_20170619.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/tests/testfiles/g15_xrs_2s_20170619_20170619.nc
--------------------------------------------------------------------------------
/tests/testfiles/mvn_euv_l2_bands_20170619_v09_r03.cdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/tests/testfiles/mvn_euv_l2_bands_20170619_v09_r03.cdf
--------------------------------------------------------------------------------
/tests/testfiles/mvn_swe_l2_svyspec_20170619_v04_r04.cdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MAVENSDC/PyTplot/f7fb3042898a52f8289b9d8c60ffae38949cad04/tests/testfiles/mvn_swe_l2_svyspec_20170619_v04_r04.cdf
--------------------------------------------------------------------------------