├── .gitattributes ├── .github └── workflows │ ├── black.yml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST ├── README.md ├── build └── lib │ └── gfspy │ ├── __init__.py │ ├── decode.py │ ├── gfspy.py │ └── test.py ├── dist ├── getgfs-0.0.0.tar.gz ├── getgfs-0.0.1.tar.gz ├── getgfs-0.0.2.tar.gz ├── getgfs-0.0.3.tar.gz ├── getgfs-0.0.4.tar.gz ├── getgfs-0.0.5.tar.gz ├── getgfs-0.0.6.tar.gz ├── getgfs-0.0.7.tar.gz ├── getgfs-0.0.8.tar.gz └── getgfs-1.0.0.tar.gz ├── docs ├── Makefile ├── build │ ├── doctrees │ │ ├── environment.pickle │ │ ├── getgfs.doctree │ │ ├── gfspy.doctree │ │ ├── index.doctree │ │ └── modules.doctree │ └── html │ │ ├── .buildinfo │ │ ├── _sources │ │ ├── getgfs.rst.txt │ │ ├── gfspy.rst.txt │ │ ├── index.rst.txt │ │ └── modules.rst.txt │ │ ├── _static │ │ ├── alabaster.css │ │ ├── basic.css │ │ ├── css │ │ │ ├── badge_only.css │ │ │ ├── fonts │ │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ ├── fontawesome-webfont.woff2 │ │ │ │ ├── lato-bold-italic.woff │ │ │ │ ├── lato-bold-italic.woff2 │ │ │ │ ├── lato-bold.woff │ │ │ │ ├── lato-bold.woff2 │ │ │ │ ├── lato-normal-italic.woff │ │ │ │ ├── lato-normal-italic.woff2 │ │ │ │ ├── lato-normal.woff │ │ │ │ └── lato-normal.woff2 │ │ │ └── theme.css │ │ ├── custom.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── jquery-3.5.1.js │ │ ├── jquery.js │ │ ├── js │ │ │ ├── badge_only.js │ │ │ ├── html5shiv-printshiv.min.js │ │ │ ├── html5shiv.min.js │ │ │ └── theme.js │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.3.1.js │ │ └── underscore.js │ │ ├── genindex.html │ │ ├── getgfs.html │ │ ├── gfspy.html │ │ ├── index.html │ │ ├── modules.html │ │ ├── objects.inv │ │ ├── py-modindex.html │ │ ├── search.html │ │ └── searchindex.js ├── make.bat └── source │ ├── conf.py │ ├── getgfs.rst │ ├── index.rst │ └── modules.rst ├── getgfs ├── __init__.py ├── __pycache__ │ └── gfspy.cpython-38.pyc ├── atts │ └── 0p25_1hr.json ├── config.json ├── decode.py ├── getgfs.py └── test.py ├── logo.png ├── requirements.txt ├── setup.cfg └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/black.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-python@v2 11 | - uses: psf/black@stable 12 | with: 13 | args: ". --check" 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [push] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Prepare repo 8 | uses: actions/checkout@master 9 | - name: Test 10 | uses: onichandame/python-test-action@master 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | gfspy/.DS_Store 3 | .DS_Store 4 | *.pyc 5 | *.pyc 6 | b 7 | build/ 8 | build/lib/getgfs/__init__.py 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/psf/black 3 | rev: 20.8b1 # Replace by any tag/version: https://github.com/psf/black/tags 4 | hooks: 5 | - id: black 6 | language_version: python3 # Should be a command that runs python3.6+ 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at js2430@cam.ac.uk. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to gfspy 2 | Thank you for taking the time to contribute and for checking out these guidelines. 3 | ## Table of content 4 | 5 | [Have a problem?](#have-a-problem) 6 | 7 | [Helping out](#helping-out) 8 | - [What you need to know](#what-do-i-need-to-know-before-i-start) 9 | - [The contribution process](#the-contribution-process) 10 | - [Comments and docstrings](#comments-and-docstrings) 11 | - [Autoformatting with black](#autoformatting-with-black) 12 | - [Building sphinx documentation](#building-sphinx-documentation) 13 | 14 | ## Have a problem? 15 | If you have a problem you have thre courses of action: 16 | 1. Ask for help - if you aren't sure how something works please check out the documentation (work in progress) and if you don't find an answer there start a [discussion](https://github.com/jagoosw/gfspy/discussions) 17 | 2. Open an issue - if you think there is a problem with the software then please open an [issue](https://github.com/jagoosw/gfspy/issues) (if you have a question please use the discussion instead of issues) 18 | 3. Fix it - if you know how to fix your problem then please follow the contribution guidlines below 19 | 20 | ## Helping out 21 | We are always looking for help with this project, the main need is more coding but if you can't do that please get in touch as we have lots of holes in the (documentation)[] and could do with administrative help too. 22 | ### What do I need to know before I start 23 | Although this project didn't have firm conventions from the start we are trying to formalise them to make the code more readable and easier to modify. Some pointers for now are: 24 | - Functionality that requires more than one class method should be in their own file 25 | - Try to stick to the [black](https://github.com/psf/black#the-black-code-style) coding style, instructions below for automation 26 | - We use [Sphinx](https://www.sphinx-doc.org/en/master/) for API documentation so comments must be in a very specific format 27 | - **When you modify code this usually has a knock on effect. This means when you make a change you should try to make it backwards compatiable or go through the other files and modify them to accomodate your changes. This usually includes modifcation to `example.py`, `statistical.py` (this could be complicated so please contact us to check how we want the changes implimented in it), `stats_example.py`, `example.ipynb`, `stats_example.ipynb` and `main.py` if you have made changes in another file. If your code includes a new functionality you should change the examples to be able to demonstrait that.** 28 | - We have a [code of conduct](https://github.com/cuspaceflight/gfspy/blob/main/CODE_OF_CONDUCT.md). TLDR - be nice to other people. 29 | 30 | ### The contribution process 31 | 32 | 1. If you are a first time contributor 33 | - Go to the project [home page](https://github.com/jagoosw/gfspy) and click "fork" in the top right of the page 34 | - Clone the repository to your computer either with the [desktop app](https://desktop.github.com/) or by 35 | 36 | ```git clone https://github.com/your-username/gfspy.git``` 37 | - Add the upstream by going into the directory (`cd gfspy`) and then 38 | - 39 | ```git remote add upstream https://github.com/jagoosw/gfspy.git``` 40 | 2. Develop your contribution: 41 | - Update to get the latest changes by pulling it in with the website or 42 | 43 | ```git checkout main``` 44 | 45 | ```git pull upstream main``` 46 | - Create a branch for your contribution with the desktop app or 47 | 48 | ```git checkout -b branch-name``` 49 | - Create and add your contributions locally (`git add` and `git commit`) 50 | 3. Test your code 51 | - Before you push your code please please please run thorough tests on it so the reviewers don't have to 52 | - This should include (at the minimum) running an example file and the stats example (because this sort of tests for edge cases randomly) and if you have made a change that changes the physics of the simulation a thorough analysis of the difference in results to determine if it is implimented correctly 53 | - If you have added something that can be tested on its own please do a thorough test as a unit (if you need inspiration for a testing stratagy please make a discussion). For example if you have added a new recovery method that has dependance on altitude and velocity make a file where you import the class and request the forces from it over a range of velocities and altitudes and see if it is as you would expect. 54 | - Keep a record of these teste (e.g. screenshots of outputs showing problems or expected behaviours) 55 | 5. Submit your contribution 56 | - Push your changes to your fork on github 57 | 58 | ``git push origin branch-name`` 59 | 60 | - Go to the your Github page (`https://github.com/your-username/gfspy`) and onto the branch where there will be a green button asking if you want to make a pull request, alternativly click pull request in the top bar 61 | - Create a pull request from your branch to the main branch of `https://github.com/jagoosw/gfspy` with a clear title, explanation of what your code does and any maths/physics that are not obvious that you have implimented and details of the tests you ran and their results (example plots etc. would be nice especially if they are different before and after your change) 62 | 63 | 6. Review 64 | - There is now an automatic test case that runs a shortened version of the example file. This is to check that your changes do not break the core functionality and so if you change something core (e.g. how aeros work) then the test case will fail. Please check the reason that your test failed and if it is because one of the tests actually fails (rather than the test not being able to run) then contact me and we will look at if the test needs changing. 65 | - We will have a look at the pull request as soon as we can and go through it, possibly testing the code ourselves 66 | - We are likely to suggest changes to style or funcitonality (please don't be offended, we have spent so much of our time working on it we don't want anything to break or be wrong) 67 | - Once this is done we will pull it into the main branch 68 | 69 | 70 | #### Comments and docstrings 71 | Please try to document your code so you and everyone else knows whats going on. This should at a minimum include adding or updating a [Google style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) docstring for each function/method and class. A few points to note are: 72 | - Docstrings must be surrounded by `"""` not `'''` 73 | - Other comments should not use `"""` as they will show up in random places in the documentation 74 | - Docstrings are whitespace sensiative - you must leave a line space between sections (follow the examples closely) 75 | - Titles (like `Note`) must have a line of `-` under them that is as long as the line (e.g. `Note↵----`) 76 | Other comments to help with your code (e.g. descriptions of what sections do) should ise `#` sytle comments - please use often (we haven't been good at this) 77 | 78 | #### Autoformatting with black 79 | To make the code cleaner and more consistent we are starting to use black autoformatting. To use `pip install black` and then `black director-name`. For more information see the link above. At some point we will transition to have git force you to use this before in which case you will need to install [pre-commit](https://pre-commit.com/). 80 | 81 | #### Building sphinx documentation 82 | When you make changes these are not automatically added to the documentation (especially since the docs work out of a different repo). You will need to [install sphinx](https://www.sphinx-doc.org/en/master/usage/installation.html) then enter `docs/` and then `make html` to update the docs. 83 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | setup.cfg 3 | setup.py 4 | getgfs/__init__.py 5 | getgfs/decode.py 6 | getgfs/getgfs.py 7 | getgfs/test.py 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # getgfs 2 | [![Tests](https://github.com/jagoosw/getgfs/actions/workflows/test.yml/badge.svg)](https://github.com/jagoosw/getgfs/actions/workflows/test.yml)[![Black Linting](https://github.com/jagoosw/getgfs/actions/workflows/black.yml/badge.svg)](https://github.com/jagoosw/getgfs/actions/workflows/black.yml)[![DOI](https://zenodo.org/badge/340361899.svg)](https://zenodo.org/badge/latestdoi/340361899) 3 | 4 | ![Logo - Cloud with sun emerging from behind with text 'GetGFS' underneath](logo.png) 5 | 6 | getgfs extracts weather forecast variables from the NOAA GFS forecast in a pure python, no obscure dependencies way. Currently you can: 7 | - "Connect" to a forecast 8 | - Search the variables 9 | - Download variables for any time range, longitude, latitude and altitude 10 | - Download "wind profiles" where you get an interpolation object for the u and v wind components by altitude 11 | 12 | For full documentation please see the [docs](https://getgfs.readthedocs.io/en/latest/) 13 | 14 | # Installing 15 | Installation is simple with PyPi: 16 | 17 | `pip install getgfs` 18 | 19 | ## Requirements 20 | The required libraries (installed by PyPi) are: 21 | 22 | ```scipy, requests, fuzzywuzzy, numpy, python_dateutil, regex``` 23 | 24 | I have tried to ensure that these are well maintained and work across platforms (as this was the motive for writing this library). 25 | 26 | # About 27 | 28 | The incentive to write this library was that the current method to get any variable was to download and extract information from a grib file. This requires you to use the ECMWF's `ecCodes` which [doesn't work on Windows](https://github.com/ecmwf/eccodes-python#system-dependencies). To get around this the [OpenDAP](https://nomads.ncep.noaa.gov/) version of the forecast is used and a custom decoder reads the downloaded files. 29 | 30 | Previous Python projects that attempted this do not fulfil all the requirements, mainly being an importable library. Acknowledgment must be made to [albertotb](https://github.com/albertotb)'s project [get-gfs](https://github.com/albertotb/get-gfs) for providing the first foothold along the way. 31 | 32 | # Usage 33 | The library is straight forward to use. To get started create a Forecast object by: 34 | 35 | ``` 36 | >>>import getgfs 37 | >>>f=getgfs.Forecast("0p25") 38 | ``` 39 | 40 | You can choose the resolution to be `0p25`, `0p50` or `1p00` and for the `0p25` forecast you can optional specify a shorter time step by adding `1hr` after. 41 | 42 | First to find what variable you are looking for use the search function, for example if I want the wind speed I could search for "wind": 43 | 44 | ``` 45 | >>>f.search("wind") 46 | [('gustsfc', '** surface wind speed (gust) [m/s] ', 100), ('ugrdprs', '** (1000 975 950 925 900.. 7 5 3 2 1) u-component of wind [m/s] ', 125), ('ugrd_1829m', '** 1829 m above mean sea level u-component of wind [m/s] ', 125), ... 47 | ``` 48 | 49 | So now I can see I might want "gustsfc". Now if I want the wind speed at N70.1 W94.7 at 5:30 on the 27th of February (only forecasts going back around a week are available and future times available depend on the forecast - look for f.times) I could do: 50 | 51 | ``` 52 | >>>res=f.get(["gustsfc"],"20210227 5:30", 70.1,-94.7) 53 | >>>res.variables["gustsfc"].data 54 | array([[[18.808477]]]) 55 | ``` 56 | 57 | You can get more information (e.g. what is the units of this) by exploring the variables information 58 | 59 | ```>>>f.variables["gustsfc"] 60 | {'_FillValue': 9.999e+20, 'missing_value': 9.999e+20, 'long_name': '** surface wind speed (gust) [m/s] ', 'level_dependent': False} 61 | ``` 62 | 63 | You can also get multiple variables by including more names in the list or a range of positions by using "'[min_lat:max_lat]'" type strings in place of the position parameters. 64 | 65 | 66 | ## Contributing 67 | Please see [contributing](CONTRIBUTING.md) for more information. 68 | 69 | # Todo 70 | - Add historical forecasts from https://www.ncei.noaa.gov/thredds/dodsC/model-gfs-004-files-old/202003/20200328/gfs_4_20200328_1800_384.grb2.das 71 | - Add export to .nc file with netcdf4 (maybe an optional dependency) 72 | - Add purge missing/unreliable (missing/unreliable fill values are provided but have to iterate through data to check probably) 73 | -------------------------------------------------------------------------------- /build/lib/gfspy/__init__.py: -------------------------------------------------------------------------------- 1 | from .gfspy import * -------------------------------------------------------------------------------- /build/lib/gfspy/decode.py: -------------------------------------------------------------------------------- 1 | import re 2 | import numpy as np 3 | 4 | 5 | class Variable: 6 | def __init__(self, name, coords, data): 7 | self.name = name 8 | self.coords = coords 9 | self.data = data 10 | 11 | def __str__(self): 12 | print(type(self)) 13 | return self.name 14 | 15 | 16 | class Coordinate: 17 | def __init__(self, name, values): 18 | self.name = name 19 | self.values = values 20 | 21 | def __str__(self): 22 | print(type(self)) 23 | return self.name 24 | 25 | 26 | class File: 27 | def __init__(self, text): 28 | text = text.splitlines() 29 | # Get variable name and dimensionality 30 | ind_head = 0 31 | variables = [] 32 | while ind_head < len(text): 33 | variable_name = re.findall("(.*?), ", text[ind_head])[0] 34 | dims = re.findall("\[(.*?)\]", text[ind_head]) 35 | dims.reverse() 36 | lines_data = 0 37 | for dim in dims[1:]: 38 | lines_data = int(dim) * (lines_data + 1) 39 | dims.reverse() 40 | 41 | lines_meta = len(dims) * 2 # Starts +1 from end of lines data 42 | name_line = True 43 | coords = [] 44 | for line in text[ 45 | ind_head + 2 + lines_data : ind_head + 3 + lines_data + lines_meta 46 | ]: 47 | if name_line: 48 | name = re.findall("(.*?), ", line)[0] 49 | name_line = False 50 | else: 51 | coords.append( 52 | Coordinate(name, [float(v[:-1]) for v in line.split()]) 53 | ) 54 | name_line = True 55 | 56 | data = np.zeros(tuple([int(d) for d in dims])) 57 | data[:] = np.nan 58 | for line in text[ind_head + 1 : ind_head + 1 + lines_data - 1]: 59 | if len(line) > 0 and line[0] == "[": 60 | position = [int(v) for v in re.findall("\[(.*?)\]", line)] 61 | values = line.split()[1:] 62 | if len(values) > 1: 63 | for ind, value in enumerate(values): 64 | if value[-1] == ",": 65 | value = value[:-1] 66 | data = replace_val(data, float(value), position + [ind]) 67 | else: 68 | data = replace_val(data, float(values[0]), position) 69 | 70 | coords = {c.name: c for c in coords} 71 | variables.append(Variable(variable_name, coords, data)) 72 | 73 | ind_head += lines_data + lines_meta + 2 74 | 75 | self.variables = {v.name: v for v in variables} 76 | 77 | def __str__(self): 78 | print(type(self)) 79 | return "File containing %s" % self.variables.keys() 80 | 81 | 82 | def replace_val(arr, val, position): 83 | if not isinstance(position, list): 84 | raise TypeError("Wrong type entered for replacement position") 85 | # I wish I could find a proper way todo this, np.put only works for 1D arrays 86 | if len(position) == 1: 87 | arr[position[0]] = val 88 | elif len(position) == 2: 89 | arr[position[0]][position[1]] = val 90 | elif len(position) == 3: 91 | arr[position[0]][position[1]][position[2]] = val 92 | elif len(position) == 4: 93 | arr[position[0]][position[1]][position[2]][position[3]] = val 94 | else: 95 | raise ValueError( 96 | "Number of dimensions for value replacement not supported, please edit and make pull request it will be very easy" 97 | ) 98 | 99 | return arr 100 | -------------------------------------------------------------------------------- /build/lib/gfspy/gfspy.py: -------------------------------------------------------------------------------- 1 | """gfspy - a library for extracting weather forecast variables from the NOAA GFS forecast in a pure python, no obscure dependencies way 2 | """ 3 | import requests, json, os, re, dateutil.parser, sys 4 | from datetime import datetime, timedelta 5 | import numpy as np 6 | from scipy.interpolate import interp1d 7 | from .decode import * 8 | 9 | try: 10 | from fuzzywuzzy import fuzz 11 | except: 12 | pass 13 | 14 | __copyright__ = """ 15 | gfspy - A library that extracts information from the NOAA GFS forecast without using .grb2 files 16 | Copyright (C) 2021 Jago Strong-Wright 17 | 18 | This program is free software: you can redistribute it and/or modify 19 | it under the terms of the GNU General Public License as published by 20 | the Free Software Foundation, either version 3 of the License, or 21 | (at your option) any later version. 22 | 23 | This program is distributed in the hope that it will be useful, 24 | but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | GNU General Public License for more details. 27 | 28 | You should have received a copy of the GNU General Public License 29 | along with this program. If not, see .""" 30 | 31 | route = os.path.abspath(os.path.dirname(__file__)) 32 | 33 | url = "https://nomads.ncep.noaa.gov/dods/gfs_{res}{step}/gfs{date}/gfs_{res}{step}_{hour:02d}z.{info}" 34 | config_file = "%s/config.json" % route 35 | attribute_file = "%s/atts/{res}{step}.json" % route 36 | 37 | if not os.path.isdir("%s/atts" % route): 38 | os.makedirs("%s/atts" % route) 39 | if not os.path.isfile(config_file): 40 | with open(config_file, "w+") as f: 41 | json.dump({"saved_atts": ["Na"]}, f) 42 | 43 | 44 | class Forcast: 45 | """Object that can be manipulated to get forcast information""" 46 | 47 | def __init__(self, resolution="0p25", timestep=""): 48 | """Setting up the forcast object by specifying the forcast type 49 | 50 | Args: 51 | resolution (str, optional): The forcast resulution, choices are 1p00, 0p50 and 0p25. Defaults to "0p25". 52 | timestep (str, optional): The timestep of the forcast to use, most do not have a choice but 0p25 can be 3hr (default) or 1hr. Defaults to "". 53 | """ 54 | if timestep != "": 55 | timestep = "_" + timestep 56 | 57 | if resolution not in ["1p00", "0p50", "0p25"]: 58 | raise ValueError( 59 | "You have entered an invalid forcast resulution, the choices are 1p00, 0p50 and 0p25. You entered %s" 60 | % resolution 61 | ) 62 | if (timestep != "" and resolution != "0p25") or (timestep not in ["_1hr",""] and resolution == "0p25"): 63 | raise ValueError( 64 | "You have entered an invalid forcast timestep, the only choice is 1hr for 0p25 forcasts or the default. You entered %s" 65 | % timestep 66 | ) 67 | self.resolution = resolution 68 | self.timestep = timestep 69 | self.times, self.coords, self.variables = get_attributes(resolution, timestep) 70 | 71 | def get(self, variables, date_time, lat, lon): 72 | """Returns the latest forcast available for the requested date and time 73 | 74 | Note: 75 | ---- 76 | - "raw" since you have to put indexes in rather than coordinates and it returns a file object rather than a processed file 77 | - If a variable has level dependance, you get all the levels - it seems extremely hard to impliment otherwise 78 | 79 | Args: 80 | variables (list): list of required variables by short name 81 | date_time (string): datetime requested 82 | lat (string or number): latitude in the format "[min:max]" or a single value 83 | lon (string or number): longitude in the format "[min:max]" or a single value 84 | """ 85 | 86 | # Get forcast date run, date, time 87 | forcast_date, forcast_time, query_time = self.datetime_to_forcast(date_time) 88 | 89 | # Get latitude 90 | lat = self.value_input_to_index("lat", lat) 91 | 92 | # Get longitude 93 | lon = self.value_input_to_index("lon", lon) 94 | 95 | # Get lev 96 | lev = "[0:%s]" % int( 97 | (self.coords["lev"]["minimum"] - self.coords["lev"]["maximum"]) 98 | / self.coords["lev"]["resolution"] 99 | ) 100 | 101 | # Make query 102 | query = "" 103 | for variable in variables: 104 | if variable not in self.variables.keys(): 105 | raise ValueError( 106 | "The variable {name} is not a valid choice for this weather model".format( 107 | name=variable 108 | ) 109 | ) 110 | if self.variables[variable]["level_dependent"] == True and lev == []: 111 | raise ValueError( 112 | "The variable {name} requires the altitude/level to be defined".format( 113 | name=variable 114 | ) 115 | ) 116 | elif self.variables[variable]["level_dependent"] == True: 117 | query += "," + variable + query_time + lev + lat + lon 118 | else: 119 | query += "," + variable + query_time + lat + lon 120 | 121 | query = query[1:] 122 | 123 | r = requests.get( 124 | url.format( 125 | res=self.resolution, 126 | step=self.timestep, 127 | date=forcast_date, 128 | hour=int(forcast_time), 129 | info="ascii?{query}".format(query=query), 130 | ) 131 | ) 132 | if r.status_code != 200: 133 | raise Exception( 134 | """The altitude pressure forcast information could not be downloaded. 135 | This error should never occure but it may be helpful to know the requested information was: 136 | - Forcast date: {f_date} 137 | - Forcast time: {f_time} 138 | - Query time: {q_time} 139 | - Latitude: {lat} 140 | - Longitude: {lon}""".format( 141 | f_date=forcast_date, 142 | f_time=forcast_time, 143 | q_time=query_time, 144 | lat=lat, 145 | lon=lon, 146 | ) 147 | ) 148 | 149 | return File(r.text) 150 | 151 | def datetime_to_forcast(self, date_time): 152 | earliest_available = hour_round(datetime.utcnow() - timedelta(days=7)) 153 | latest_available = hour_round( 154 | datetime.utcnow() 155 | - timedelta( 156 | hours=datetime.utcnow().hour 157 | - 6 * (datetime.utcnow().hour // 6) 158 | - int(self.times["grads_size"]) * int(self.times["grads_step"][0]), 159 | minutes=datetime.utcnow().minute, 160 | seconds=datetime.utcnow().second, 161 | microseconds=datetime.utcnow().microsecond, 162 | ) 163 | ) 164 | desired_date = dateutil.parser.parse(date_time) 165 | latest_forcast = latest_available - timedelta( 166 | hours=int(self.times["grads_size"]) * int(self.times["grads_step"][0]) 167 | ) 168 | if earliest_available < desired_date < latest_available: 169 | query_forcast = latest_forcast 170 | while desired_date < query_forcast: 171 | query_forcast -= timedelta(hours=6) 172 | 173 | forcast_date = query_forcast.strftime("%Y%m%d") 174 | forcast_time = query_forcast.strftime("%H") 175 | 176 | query_time = "[{t_ind}]".format( 177 | t_ind=round( 178 | (desired_date - query_forcast).seconds 179 | / (int(self.times["grads_step"][0]) * 60 * 60) 180 | ) 181 | ) 182 | 183 | else: 184 | raise ValueError( 185 | "Datetime requested ({dt}) is not available the moment, usually only the last weeks worth of forcasts are available and this model only extends {hours} hours forward.\nThis error may be caused by an uninterpretable datetime format.".format( 186 | hours=int(self.times["grads_size"]) 187 | * int(self.times["grads_step"][0]), 188 | dt=dateutil.parser.parse(date_time), 189 | ) 190 | ) 191 | 192 | return forcast_date, forcast_time, query_time 193 | 194 | def value_input_to_index(self, coord, inpt): 195 | if isinstance(inpt, str): 196 | if inpt[0] == "[" and inpt[-1] == "]" and ":" in inpt: 197 | val_1 = float(re.findall(r"\[(.*?):", inpt)) 198 | val_2 = float(re.findall(r"\:(.*?)]", inpt)) 199 | val_min = self.value_to_index(coord, min(val_1, val_2)) 200 | val_max = self.value_to_index(coord, max(val_1, val_2)) 201 | ind = "[%s:%s]" % (val_min, val_max) 202 | else: 203 | try: 204 | inpt = float(inpt) # isnumeric apparently doesn't work for floats 205 | except: 206 | raise ValueError( 207 | "The format of the %s variable was incorrect, it must either be a single number or a range in the format [min:max]. You entered '%s'" 208 | % (coord, inpt) 209 | ) 210 | ind = "[%s]" % self.value_to_index(coord, inpt) 211 | else: 212 | ind = "[%s]" % self.value_to_index(coord, inpt) 213 | 214 | return ind 215 | 216 | def value_to_index(self, coord, value): 217 | possibles = [ 218 | float(self.coords[coord]["resolution"]) * n 219 | + float(self.coords[coord]["minimum"]) 220 | for n in range(0, int(self.coords[coord]["grads_size"])) 221 | ] 222 | closest = min(possibles, key=lambda x: abs(x - value)) 223 | return possibles.index(closest) 224 | 225 | def search_names(self, variable, sensetivity=80): 226 | if "fuzzywuzzy.fuzz" not in sys.modules: 227 | raise RuntimeError( 228 | "You can not use search_name without fuzzywuzzy installed, please `pip install fuzzywuzzy`. Other functionality is still available" 229 | ) 230 | 231 | possibles = [] 232 | for var in self.variables.keys(): 233 | if "long_name" in self.variables[var].keys(): 234 | ln = fuzz.partial_ratio(self.variables[var]["long_name"], variable) 235 | sn = fuzz.partial_ratio(var, variable) 236 | if ln > sensetivity or sn > sensetivity: 237 | possibles.append((var, self.variables[var]["long_name"], ln + sn)) 238 | 239 | possibles = sorted(possibles, key=lambda tup: tup[2]) 240 | return possibles 241 | 242 | def get_windprofile(self, date_time, lat, lon): 243 | info = self.get( 244 | ["ugrdprs", "vgrdprs", "ugrd2pv", "vgrd2pv", "hgtsfc", "hgtprs"], 245 | date_time, 246 | lat, 247 | lon, 248 | ) 249 | 250 | u_wind = list(info.variables["ugrdprs"].data.flatten()) + list( 251 | info.variables["ugrd2pv"].data.flatten() 252 | ) 253 | v_wind = list(info.variables["vgrdprs"].data.flatten()) + list( 254 | info.variables["vgrd2pv"].data.flatten() 255 | ) 256 | 257 | # at the altitudes we are concerned with the geopotential height and altitude are within 0.5km of eachother 258 | alts = list(info.variables["hgtprs"].data.flatten()) + list( 259 | info.variables["hgtsfc"].data.flatten() 260 | ) 261 | 262 | return interp1d(alts, u_wind), interp1d(alts, v_wind) 263 | 264 | def __str__(self): 265 | print(type(self)) 266 | return "GFS forcast with resolution %s" % self.resolution 267 | 268 | 269 | def get_attributes(res, step): 270 | with open(config_file) as f: 271 | config = json.load(f) 272 | if "{res}{step}".format(res=res, step=step) not in config["saved_atts"]: 273 | if datetime.utcnow().hour < 6: 274 | date = datetime.utcnow() - timedelta(day=1) 275 | else: 276 | date = datetime.utcnow() 277 | r = requests.get( 278 | url.format( 279 | res=res, 280 | step=step, 281 | date=date.strftime("%Y%m%d"), 282 | hour=0, 283 | info="das", 284 | ) 285 | ) 286 | if r.status_code != 200: 287 | raise Exception("The forcast resolution and timestep was not found") 288 | search_text = re.sub("\s{2,}", "", r.text[12:-2]) 289 | raws = re.findall(r"(.*?) \{(.*?)\}", search_text) 290 | variables = {} 291 | coords = {} 292 | for var in raws: 293 | attributes = {} 294 | atts = var[1].split(";") 295 | # Extraction from a line could be simplified to a function 296 | if var[0] not in ["time", "lat", "lon", "lev"]: 297 | for att in atts: 298 | iden, val = extract_line( 299 | ["_FillValue", "missing_value", "long_name"], att 300 | ) 301 | if iden != None: 302 | attributes[iden] = val 303 | variables[var[0]] = attributes 304 | elif var[0] == "time": 305 | for att in atts: 306 | iden, val = extract_line(["grads_size", "grads_step"], att) 307 | if iden != None: 308 | attributes[iden] = val 309 | time = attributes 310 | else: 311 | for att in atts: 312 | iden, val = extract_line( 313 | ["grads_dim", "grads_size", "minimum", "maximum", "resolution"], 314 | att, 315 | ) 316 | if iden != None: 317 | attributes[iden] = val 318 | coords[var[0]] = attributes 319 | 320 | r = requests.get( 321 | url.format( 322 | res=res, 323 | step=step, 324 | date=date.today().strftime("%Y%m%d"), 325 | hour=0, 326 | info="dds", 327 | ) 328 | ) 329 | if r.status_code != 200: 330 | raise RuntimeError("The forcast resolution and timestep was not found") 331 | arrays = re.findall(r"ARRAY:\n(.*?)\n", r.text) 332 | for array in arrays: 333 | var = re.findall(r"(.*?)\[", array)[0].split()[1] 334 | if var in variables.keys(): 335 | lev_dep = False 336 | for dim in re.findall(r"(.*?)\[", array): 337 | if dim.split()[0] == "lev": 338 | lev_dep = True 339 | variables[var]["level_dependent"] = lev_dep 340 | 341 | save = {"time": time, "coords": coords, "variables": variables} 342 | with open(attribute_file.format(res=res, step=step), "w+") as f: 343 | json.dump(save, f) 344 | config["saved_atts"].append("{res}{step}".format(res=res, step=step)) 345 | with open(config_file, "w+") as f: 346 | json.dump(config, f) 347 | return time, coords, variables 348 | else: 349 | with open(attribute_file.format(res=res, step=step)) as f: 350 | data = json.load(f) 351 | return data["time"], data["coords"], data["variables"] 352 | 353 | 354 | def extract_line(possibles, line): 355 | found = -1 356 | ind = -1 357 | while found == -1 and ind < len(possibles) - 1: 358 | ind += 1 359 | found = line.find(possibles[ind]) 360 | 361 | if found != -1: 362 | if line[0:3] == "Str": 363 | return possibles[ind], line[found + len(possibles[ind]) + 2 : -1] 364 | elif line[0:3] == "Flo": 365 | return possibles[ind], float(line[found + len(possibles[ind]) + 1 :]) 366 | return None, None 367 | 368 | 369 | def hour_round(t): 370 | return t.replace(second=0, microsecond=0, minute=0, hour=t.hour) + timedelta( 371 | hours=t.minute // 30 372 | ) 373 | 374 | 375 | if __name__ == "__main__": 376 | test = False 377 | 378 | f = Forcast("0p25", "1hr") 379 | # print(f.search_names("geopotential")) 380 | u, v = f.get_windprofile("20210226 17:00", "12.5", "6.3") 381 | print(u(1000)) 382 | # print(f.value_to_index("lat", 0)) 383 | if test == True: 384 | os.remove(config_file) 385 | import shutil 386 | 387 | shutil.rmtree("%s/atts" % route) 388 | -------------------------------------------------------------------------------- /build/lib/gfspy/test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from .gfspy import * 3 | from .decode import * 4 | 5 | # Seems like these aren't actually working 6 | 7 | __copyright__ = """ 8 | gfspy - A library that extracts information from the NOAA GFS forecast without using .grb2 files 9 | Copyright (C) 2021 Jago Strong-Wright 10 | 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License 22 | along with this program. If not, see .""" 23 | 24 | example_file = """hgtprs, [5][4][2][4] 25 | [0][0][0], 143.26001, 143.26001, 143.26001, 143.26001 26 | [0][0][1], 142.28401, 142.268, 142.25201, 142.25201 27 | 28 | [0][1][0], 329.76636, 329.76636, 329.76636, 329.76636 29 | [0][1][1], 328.71036, 328.71036, 328.69437, 328.67834 30 | 31 | [0][2][0], 520.16754, 520.16754, 520.16754, 520.16754 32 | [0][2][1], 519.04755, 519.0315, 519.0155, 518.9995 33 | 34 | [0][3][0], 714.6294, 714.6294, 714.6294, 714.6294 35 | [0][3][1], 713.4134, 713.3974, 713.3974, 713.3814 36 | 37 | 38 | [1][0][0], 141.57744, 141.57744, 141.57744, 141.57744 39 | [1][0][1], 142.63344, 142.60144, 142.56944, 142.53745 40 | 41 | [1][1][0], 328.0829, 328.0829, 328.0829, 328.0829 42 | [1][1][1], 328.93088, 328.9149, 328.8829, 328.8509 43 | 44 | [1][2][0], 518.4631, 518.4631, 518.4631, 518.4631 45 | [1][2][1], 519.1191, 519.08704, 519.07104, 519.03906 46 | 47 | [1][3][0], 712.9098, 712.9098, 712.9098, 712.9098 48 | [1][3][1], 713.3578, 713.3418, 713.3258, 713.29376 49 | 50 | 51 | [2][0][0], 132.99606, 132.99606, 132.99606, 132.99606 52 | [2][0][1], 133.52406, 133.50807, 133.49207, 133.46007 53 | 54 | [2][1][0], 319.4645, 319.4645, 319.4645, 319.4645 55 | [2][1][1], 319.8485, 319.83252, 319.81653, 319.78452 56 | 57 | [2][2][0], 509.819, 509.819, 509.819, 509.819 58 | [2][2][1], 510.059, 510.043, 510.027, 510.011 59 | 60 | [2][3][0], 704.22687, 704.22687, 704.22687, 704.22687 61 | [2][3][1], 704.3229, 704.3068, 704.29083, 704.29083 62 | 63 | 64 | [3][0][0], 129.57916, 129.57916, 129.57916, 129.57916 65 | [3][0][1], 129.99516, 129.97916, 129.94716, 129.93117 66 | 67 | [3][1][0], 315.9453, 315.9453, 315.9453, 315.9453 68 | [3][1][1], 316.21732, 316.18533, 316.16934, 316.15332 69 | 70 | [3][2][0], 506.1779, 506.1779, 506.1779, 506.1779 71 | [3][2][1], 506.3059, 506.2899, 506.2739, 506.2579 72 | 73 | [3][3][0], 700.4825, 700.4825, 700.4825, 700.4825 74 | [3][3][1], 700.4665, 700.4505, 700.41846, 700.40247 75 | 76 | 77 | [4][0][0], 124.38838, 124.38838, 124.38838, 124.38838 78 | [4][0][1], 125.04438, 125.02838, 125.01238, 125.01238 79 | 80 | [4][1][0], 310.62332, 310.62332, 310.62332, 310.62332 81 | [4][1][1], 311.1353, 311.11932, 311.11932, 311.08734 82 | 83 | [4][2][0], 500.7546, 500.7546, 500.7546, 500.7546 84 | [4][2][1], 501.10663, 501.0906, 501.0906, 501.07462 85 | 86 | [4][3][0], 694.9354, 694.9354, 694.9354, 694.9354 87 | [4][3][1], 695.1274, 695.1274, 695.1114, 695.1114 88 | 89 | 90 | 91 | time, [5] 92 | 737842.0, 737842.125, 737842.25, 737842.375, 737842.5 93 | lev, [4] 94 | 1000.0, 975.0, 950.0, 925.0 95 | lat, [2] 96 | -90.0, -89.75 97 | lon, [4] 98 | 0.0, 0.25, 0.5, 0.75 99 | hgtmwl, [1][1][1] 100 | [0][0], 9504.847 101 | 102 | 103 | time, [1] 104 | 737842.0 105 | lat, [1] 106 | -90.0 107 | lon, [1] 108 | 0.0""" 109 | 110 | example = File(example_file) 111 | class TestBasics(unittest.TestCase): 112 | def test_attribute(self): 113 | self.assertEqual( 114 | Forcast("0p25").times, {"grads_size": "81", "grads_step": "3hr"} 115 | ) 116 | 117 | def test_folders(self): 118 | if not os.path.isdir("%s/atts" % route) and not os.path.isfile( 119 | config_file 120 | ): 121 | result = "Required files and folders are not being created" 122 | else: 123 | result = "Ok" 124 | self.assertEqual(result, "Ok") 125 | 126 | 127 | class Decode(unittest.TestCase): 128 | def test_variables(self): 129 | self.assertEqual( 130 | example.variables["hgtprs"].coords["time"].values, 131 | [737842.0, 737842.125, 737842.25, 737842.375, 737842.0], 132 | ) 133 | 134 | def test_data(self): 135 | self.assertEqual(example.variables["hgtmwl"].data, [[[9504.847]]]) 136 | 137 | 138 | if __name__ == "__main__": 139 | unittest.main() 140 | -------------------------------------------------------------------------------- /dist/getgfs-0.0.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.0.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-0.0.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.1.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-0.0.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.2.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-0.0.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.3.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-0.0.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.4.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-0.0.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.5.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-0.0.6.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.6.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-0.0.7.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.7.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-0.0.8.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-0.0.8.tar.gz -------------------------------------------------------------------------------- /dist/getgfs-1.0.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/dist/getgfs-1.0.0.tar.gz -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/build/doctrees/getgfs.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/doctrees/getgfs.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/gfspy.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/doctrees/gfspy.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/doctrees/modules.doctree -------------------------------------------------------------------------------- /docs/build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 0b03a950635990dd7c6a8e0fc4b71893 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/build/html/_sources/getgfs.rst.txt: -------------------------------------------------------------------------------- 1 | getgfs package 2 | ============== 3 | 4 | Submodules 5 | ---------- 6 | 7 | getgfs.decode module 8 | -------------------- 9 | 10 | .. automodule:: getgfs.decode 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | getgfs.getgfs module 16 | -------------------- 17 | 18 | .. automodule:: getgfs.getgfs 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | getgfs.test module 24 | ------------------ 25 | 26 | .. automodule:: getgfs.test 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | Module contents 32 | --------------- 33 | 34 | .. automodule:: getgfs 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | -------------------------------------------------------------------------------- /docs/build/html/_sources/gfspy.rst.txt: -------------------------------------------------------------------------------- 1 | gfspy package 2 | ============= 3 | 4 | Submodules 5 | ---------- 6 | 7 | gfspy.decode module 8 | ------------------- 9 | 10 | .. automodule:: gfspy.decode 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | gfspy.gfspy module 16 | ------------------ 17 | 18 | .. automodule:: gfspy.gfspy 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | gfspy.test module 24 | ----------------- 25 | 26 | .. automodule:: gfspy.test 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | Module contents 32 | --------------- 33 | 34 | .. automodule:: gfspy 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | -------------------------------------------------------------------------------- /docs/build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. getgfs documentation master file, created by 2 | sphinx-quickstart on Sun Feb 28 19:01:16 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to getgfs 7 | ================ 8 | 9 | getgfs extracts weather forecast variables from the NOAA GFS forecast in a pure python, no obscure dependencies way. Currently you can: 10 | 11 | - "Connect" to a forecast 12 | - Search the variables 13 | - Download variables for any time range, longitude, latitude and altitude 14 | - Download "wind profiles" where you get an interpolation object for the u and v wind components by altitude 15 | 16 | Installing 17 | ========== 18 | Installation is simple with PyPi: 19 | 20 | ```pip install getgfs``` 21 | 22 | Usage 23 | ===== 24 | The library is straight forward to use. To get started create a Forecast object by: 25 | 26 | .. code-block:: python 27 | :linenos: 28 | 29 | >>>import getgfs 30 | >>>f=getgfs.Forecast("0p25") 31 | 32 | You can chose the resolution to be `0p25`, `0p50` or `1p00` and for the `0p25` forecast you can optional specify a shorter timestep by adding `1hr` after. 33 | 34 | First to find what variable you are looking for use the search function, for example if I want the wind speed I could search for "wind": 35 | 36 | .. code-block:: python 37 | :linenos: 38 | 39 | >>>f.search("wind") 40 | [('gustsfc', '** surface wind speed (gust) [m/s] ', 100), ('ugrdprs', '** (1000 975 950 925 900.. 7 5 3 2 1) u-component of wind [m/s] ', 125), ('ugrd_1829m', '** 1829 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd_2743m', '** 2743 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd_3658m', '** 3658 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd10m', '** 10 m above ground u-component of wind [m/s] ', 125), ('ugrd20m', '** 20 m above ground u-component of wind [m/s] ', 125), ('ugrd30m', '** 30 m above ground u-component of wind [m/s] ', 125), ('ugrd40m', '** 40 m above ground u-component of wind [m/s] ', 125), ('ugrd50m', '** 50 m above ground u-component of wind [m/s] ', 125), ('ugrd80m', '** 80 m above ground u-component of wind [m/s] ', 125), ('ugrd100m', '** 100 m above ground u-component of wind [m/s] ', 125), ('ugrdsig995', '** 0.995 sigma level u-component of wind [m/s] ', 125), ('ugrd30_0mb', '** 30-0 mb above ground u-component of wind [m/s] ', 125), ('ugrd2pv', '** pv=2e-06 (km^2/kg/s) surface u-component of wind [m/s] ', 125), ('ugrdneg2pv', '** pv=-2e-06 (km^2/kg/s) surface u-component of wind [m/s] ', 125), ('ugrdpbl', '** planetary boundary layer u-component of wind [m/s] ', 125), ('ugrdtrop', '** tropopause u-component of wind [m/s] ', 125), ('vgrdprs', '** (1000 975 950 925 900.. 7 5 3 2 1) v-component of wind [m/s] ', 125), ('vgrd_1829m', '** 1829 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd_2743m', '** 2743 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd_3658m', '** 3658 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd10m', '** 10 m above ground v-component of wind [m/s] ', 125), ('vgrd20m', '** 20 m above ground v-component of wind [m/s] ', 125), ('vgrd30m', '** 30 m above ground v-component of wind [m/s] ', 125), ('vgrd40m', '** 40 m above ground v-component of wind [m/s] ', 125), ('vgrd50m', '** 50 m above ground v-component of wind [m/s] ', 125), ('vgrd80m', '** 80 m above ground v-component of wind [m/s] ', 125), ('vgrd100m', '** 100 m above ground v-component of wind [m/s] ', 125), ('vgrdsig995', '** 0.995 sigma level v-component of wind [m/s] ', 125), ('vgrd30_0mb', '** 30-0 mb above ground v-component of wind [m/s] ', 125), ('vgrd2pv', '** pv=2e-06 (km^2/kg/s) surface v-component of wind [m/s] ', 125), ('vgrdneg2pv', '** pv=-2e-06 (km^2/kg/s) surface v-component of wind [m/s] ', 125), ('vgrdpbl', '** planetary boundary layer v-component of wind [m/s] ', 125), ('vgrdtrop', '** tropopause v-component of wind [m/s] ', 125), ('hgtmwl', '** max wind geopotential height [gpm] ', 133), ('icahtmwl', '** max wind icao standard atmosphere reference height [m] ', 133), ('presmwl', '** max wind pressure [pa] ', 133), ('tmpmwl', '** max wind temperature [k] ', 133), ('ugrdmwl', '** max wind u-component of wind [m/s] ', 133), ('vgrdmwl', '** max wind v-component of wind [m/s] ', 133)] 41 | 42 | So now I can see I might want "gustsfc". Now if I want the wind speed at N70.1 W94.7 at 5:30 on the 27th of Febuary (only forecasts going back around a week are available and furutre times available depend on the forecast - look for f.times) I could do: 43 | 44 | .. code-block:: python 45 | :linenos: 46 | 47 | >>>res=f.get(["gustsfc"],"20210227 5:30", 70.1,-94.7) 48 | >>>res.variables["gustsfc"].data 49 | array([[[18.808477]]]) 50 | 51 | You can get more information (e.g. what is the units of this) by exploring the variables information 52 | 53 | .. code-block:: python 54 | :linenos: 55 | 56 | >>>f.variables["gustsfc"] 57 | {'_FillValue': 9.999e+20, 'missing_value': 9.999e+20, 'long_name': '** surface wind speed (gust) [m/s] ', 'level_dependent': False} 58 | 59 | You can also get multiple variables by including more names in the list or a range of positions by using "'[min_lat:max_lat]'" type strings in place of the position parameters. 60 | 61 | About 62 | ===== 63 | 64 | The incentive to write this library was that the current method to get any variable was to download and extract information from a grib file. This requires you to use the ECMWF's `ecCodes` which `doesn't work on Windows `_. To get around this the `OpenDAP `_ version of the forecast is used and a custom decoder reads the downloaded files. 65 | 66 | Previous Python projects that attempted this do not fulfil all the requirements, mainly being an importable library. Acknowledgment must be made to `albertotb `_'s project `get-gfs `_ for providing the first foothold along the way. 67 | 68 | 69 | Requirements 70 | ------------ 71 | The required libraries (installed by PyPi) are: 72 | 73 | ```scipy, requests, fuzzywuzzy, numpy, python_dateutil, regex``` 74 | 75 | I have tried to ensure that these are well maintained and work across platforms (as this was the motive for writing this library). 76 | 77 | Contributing 78 | ============ 79 | Please see `contributing `_ for more information. 80 | 81 | Todo 82 | ---- 83 | - Add historical forecasts from ``_ 84 | - Add export to .nc file with netcdf4 (maybe an optional dependency) 85 | - Add purge missing/unreliable (missing/unreliable fill values are provided but have to iterate through data to check probably) 86 | 87 | .. toctree:: 88 | :maxdepth: 2 89 | :caption: Contents: 90 | 91 | getgfs.rst 92 | 93 | 94 | 95 | Indices and tables 96 | ================== 97 | 98 | * :ref:`genindex` 99 | * :ref:`modindex` 100 | * :ref:`search` 101 | -------------------------------------------------------------------------------- /docs/build/html/_sources/modules.rst.txt: -------------------------------------------------------------------------------- 1 | getgfs 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | getgfs 8 | -------------------------------------------------------------------------------- /docs/build/html/_static/alabaster.css: -------------------------------------------------------------------------------- 1 | @import url("basic.css"); 2 | 3 | /* -- page layout ----------------------------------------------------------- */ 4 | 5 | body { 6 | font-family: Georgia, serif; 7 | font-size: 17px; 8 | background-color: #fff; 9 | color: #000; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | 15 | div.document { 16 | width: 940px; 17 | margin: 30px auto 0 auto; 18 | } 19 | 20 | div.documentwrapper { 21 | float: left; 22 | width: 100%; 23 | } 24 | 25 | div.bodywrapper { 26 | margin: 0 0 0 220px; 27 | } 28 | 29 | div.sphinxsidebar { 30 | width: 220px; 31 | font-size: 14px; 32 | line-height: 1.5; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #fff; 41 | color: #3E4349; 42 | padding: 0 30px 0 30px; 43 | } 44 | 45 | div.body > .section { 46 | text-align: left; 47 | } 48 | 49 | div.footer { 50 | width: 940px; 51 | margin: 20px auto 30px auto; 52 | font-size: 14px; 53 | color: #888; 54 | text-align: right; 55 | } 56 | 57 | div.footer a { 58 | color: #888; 59 | } 60 | 61 | p.caption { 62 | font-family: inherit; 63 | font-size: inherit; 64 | } 65 | 66 | 67 | div.relations { 68 | display: none; 69 | } 70 | 71 | 72 | div.sphinxsidebar a { 73 | color: #444; 74 | text-decoration: none; 75 | border-bottom: 1px dotted #999; 76 | } 77 | 78 | div.sphinxsidebar a:hover { 79 | border-bottom: 1px solid #999; 80 | } 81 | 82 | div.sphinxsidebarwrapper { 83 | padding: 18px 10px; 84 | } 85 | 86 | div.sphinxsidebarwrapper p.logo { 87 | padding: 0; 88 | margin: -10px 0 0 0px; 89 | text-align: center; 90 | } 91 | 92 | div.sphinxsidebarwrapper h1.logo { 93 | margin-top: -10px; 94 | text-align: center; 95 | margin-bottom: 5px; 96 | text-align: left; 97 | } 98 | 99 | div.sphinxsidebarwrapper h1.logo-name { 100 | margin-top: 0px; 101 | } 102 | 103 | div.sphinxsidebarwrapper p.blurb { 104 | margin-top: 0; 105 | font-style: normal; 106 | } 107 | 108 | div.sphinxsidebar h3, 109 | div.sphinxsidebar h4 { 110 | font-family: Georgia, serif; 111 | color: #444; 112 | font-size: 24px; 113 | font-weight: normal; 114 | margin: 0 0 5px 0; 115 | padding: 0; 116 | } 117 | 118 | div.sphinxsidebar h4 { 119 | font-size: 20px; 120 | } 121 | 122 | div.sphinxsidebar h3 a { 123 | color: #444; 124 | } 125 | 126 | div.sphinxsidebar p.logo a, 127 | div.sphinxsidebar h3 a, 128 | div.sphinxsidebar p.logo a:hover, 129 | div.sphinxsidebar h3 a:hover { 130 | border: none; 131 | } 132 | 133 | div.sphinxsidebar p { 134 | color: #555; 135 | margin: 10px 0; 136 | } 137 | 138 | div.sphinxsidebar ul { 139 | margin: 10px 0; 140 | padding: 0; 141 | color: #000; 142 | } 143 | 144 | div.sphinxsidebar ul li.toctree-l1 > a { 145 | font-size: 120%; 146 | } 147 | 148 | div.sphinxsidebar ul li.toctree-l2 > a { 149 | font-size: 110%; 150 | } 151 | 152 | div.sphinxsidebar input { 153 | border: 1px solid #CCC; 154 | font-family: Georgia, serif; 155 | font-size: 1em; 156 | } 157 | 158 | div.sphinxsidebar hr { 159 | border: none; 160 | height: 1px; 161 | color: #AAA; 162 | background: #AAA; 163 | 164 | text-align: left; 165 | margin-left: 0; 166 | width: 50%; 167 | } 168 | 169 | div.sphinxsidebar .badge { 170 | border-bottom: none; 171 | } 172 | 173 | div.sphinxsidebar .badge:hover { 174 | border-bottom: none; 175 | } 176 | 177 | /* To address an issue with donation coming after search */ 178 | div.sphinxsidebar h3.donation { 179 | margin-top: 10px; 180 | } 181 | 182 | /* -- body styles ----------------------------------------------------------- */ 183 | 184 | a { 185 | color: #004B6B; 186 | text-decoration: underline; 187 | } 188 | 189 | a:hover { 190 | color: #6D4100; 191 | text-decoration: underline; 192 | } 193 | 194 | div.body h1, 195 | div.body h2, 196 | div.body h3, 197 | div.body h4, 198 | div.body h5, 199 | div.body h6 { 200 | font-family: Georgia, serif; 201 | font-weight: normal; 202 | margin: 30px 0px 10px 0px; 203 | padding: 0; 204 | } 205 | 206 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 207 | div.body h2 { font-size: 180%; } 208 | div.body h3 { font-size: 150%; } 209 | div.body h4 { font-size: 130%; } 210 | div.body h5 { font-size: 100%; } 211 | div.body h6 { font-size: 100%; } 212 | 213 | a.headerlink { 214 | color: #DDD; 215 | padding: 0 4px; 216 | text-decoration: none; 217 | } 218 | 219 | a.headerlink:hover { 220 | color: #444; 221 | background: #EAEAEA; 222 | } 223 | 224 | div.body p, div.body dd, div.body li { 225 | line-height: 1.4em; 226 | } 227 | 228 | div.admonition { 229 | margin: 20px 0px; 230 | padding: 10px 30px; 231 | background-color: #EEE; 232 | border: 1px solid #CCC; 233 | } 234 | 235 | div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { 236 | background-color: #FBFBFB; 237 | border-bottom: 1px solid #fafafa; 238 | } 239 | 240 | div.admonition p.admonition-title { 241 | font-family: Georgia, serif; 242 | font-weight: normal; 243 | font-size: 24px; 244 | margin: 0 0 10px 0; 245 | padding: 0; 246 | line-height: 1; 247 | } 248 | 249 | div.admonition p.last { 250 | margin-bottom: 0; 251 | } 252 | 253 | div.highlight { 254 | background-color: #fff; 255 | } 256 | 257 | dt:target, .highlight { 258 | background: #FAF3E8; 259 | } 260 | 261 | div.warning { 262 | background-color: #FCC; 263 | border: 1px solid #FAA; 264 | } 265 | 266 | div.danger { 267 | background-color: #FCC; 268 | border: 1px solid #FAA; 269 | -moz-box-shadow: 2px 2px 4px #D52C2C; 270 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 271 | box-shadow: 2px 2px 4px #D52C2C; 272 | } 273 | 274 | div.error { 275 | background-color: #FCC; 276 | border: 1px solid #FAA; 277 | -moz-box-shadow: 2px 2px 4px #D52C2C; 278 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 279 | box-shadow: 2px 2px 4px #D52C2C; 280 | } 281 | 282 | div.caution { 283 | background-color: #FCC; 284 | border: 1px solid #FAA; 285 | } 286 | 287 | div.attention { 288 | background-color: #FCC; 289 | border: 1px solid #FAA; 290 | } 291 | 292 | div.important { 293 | background-color: #EEE; 294 | border: 1px solid #CCC; 295 | } 296 | 297 | div.note { 298 | background-color: #EEE; 299 | border: 1px solid #CCC; 300 | } 301 | 302 | div.tip { 303 | background-color: #EEE; 304 | border: 1px solid #CCC; 305 | } 306 | 307 | div.hint { 308 | background-color: #EEE; 309 | border: 1px solid #CCC; 310 | } 311 | 312 | div.seealso { 313 | background-color: #EEE; 314 | border: 1px solid #CCC; 315 | } 316 | 317 | div.topic { 318 | background-color: #EEE; 319 | } 320 | 321 | p.admonition-title { 322 | display: inline; 323 | } 324 | 325 | p.admonition-title:after { 326 | content: ":"; 327 | } 328 | 329 | pre, tt, code { 330 | font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 331 | font-size: 0.9em; 332 | } 333 | 334 | .hll { 335 | background-color: #FFC; 336 | margin: 0 -12px; 337 | padding: 0 12px; 338 | display: block; 339 | } 340 | 341 | img.screenshot { 342 | } 343 | 344 | tt.descname, tt.descclassname, code.descname, code.descclassname { 345 | font-size: 0.95em; 346 | } 347 | 348 | tt.descname, code.descname { 349 | padding-right: 0.08em; 350 | } 351 | 352 | img.screenshot { 353 | -moz-box-shadow: 2px 2px 4px #EEE; 354 | -webkit-box-shadow: 2px 2px 4px #EEE; 355 | box-shadow: 2px 2px 4px #EEE; 356 | } 357 | 358 | table.docutils { 359 | border: 1px solid #888; 360 | -moz-box-shadow: 2px 2px 4px #EEE; 361 | -webkit-box-shadow: 2px 2px 4px #EEE; 362 | box-shadow: 2px 2px 4px #EEE; 363 | } 364 | 365 | table.docutils td, table.docutils th { 366 | border: 1px solid #888; 367 | padding: 0.25em 0.7em; 368 | } 369 | 370 | table.field-list, table.footnote { 371 | border: none; 372 | -moz-box-shadow: none; 373 | -webkit-box-shadow: none; 374 | box-shadow: none; 375 | } 376 | 377 | table.footnote { 378 | margin: 15px 0; 379 | width: 100%; 380 | border: 1px solid #EEE; 381 | background: #FDFDFD; 382 | font-size: 0.9em; 383 | } 384 | 385 | table.footnote + table.footnote { 386 | margin-top: -15px; 387 | border-top: none; 388 | } 389 | 390 | table.field-list th { 391 | padding: 0 0.8em 0 0; 392 | } 393 | 394 | table.field-list td { 395 | padding: 0; 396 | } 397 | 398 | table.field-list p { 399 | margin-bottom: 0.8em; 400 | } 401 | 402 | /* Cloned from 403 | * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 404 | */ 405 | .field-name { 406 | -moz-hyphens: manual; 407 | -ms-hyphens: manual; 408 | -webkit-hyphens: manual; 409 | hyphens: manual; 410 | } 411 | 412 | table.footnote td.label { 413 | width: .1px; 414 | padding: 0.3em 0 0.3em 0.5em; 415 | } 416 | 417 | table.footnote td { 418 | padding: 0.3em 0.5em; 419 | } 420 | 421 | dl { 422 | margin: 0; 423 | padding: 0; 424 | } 425 | 426 | dl dd { 427 | margin-left: 30px; 428 | } 429 | 430 | blockquote { 431 | margin: 0 0 0 30px; 432 | padding: 0; 433 | } 434 | 435 | ul, ol { 436 | /* Matches the 30px from the narrow-screen "li > ul" selector below */ 437 | margin: 10px 0 10px 30px; 438 | padding: 0; 439 | } 440 | 441 | pre { 442 | background: #EEE; 443 | padding: 7px 30px; 444 | margin: 15px 0px; 445 | line-height: 1.3em; 446 | } 447 | 448 | div.viewcode-block:target { 449 | background: #ffd; 450 | } 451 | 452 | dl pre, blockquote pre, li pre { 453 | margin-left: 0; 454 | padding-left: 30px; 455 | } 456 | 457 | tt, code { 458 | background-color: #ecf0f3; 459 | color: #222; 460 | /* padding: 1px 2px; */ 461 | } 462 | 463 | tt.xref, code.xref, a tt { 464 | background-color: #FBFBFB; 465 | border-bottom: 1px solid #fff; 466 | } 467 | 468 | a.reference { 469 | text-decoration: none; 470 | border-bottom: 1px dotted #004B6B; 471 | } 472 | 473 | /* Don't put an underline on images */ 474 | a.image-reference, a.image-reference:hover { 475 | border-bottom: none; 476 | } 477 | 478 | a.reference:hover { 479 | border-bottom: 1px solid #6D4100; 480 | } 481 | 482 | a.footnote-reference { 483 | text-decoration: none; 484 | font-size: 0.7em; 485 | vertical-align: top; 486 | border-bottom: 1px dotted #004B6B; 487 | } 488 | 489 | a.footnote-reference:hover { 490 | border-bottom: 1px solid #6D4100; 491 | } 492 | 493 | a:hover tt, a:hover code { 494 | background: #EEE; 495 | } 496 | 497 | 498 | @media screen and (max-width: 870px) { 499 | 500 | div.sphinxsidebar { 501 | display: none; 502 | } 503 | 504 | div.document { 505 | width: 100%; 506 | 507 | } 508 | 509 | div.documentwrapper { 510 | margin-left: 0; 511 | margin-top: 0; 512 | margin-right: 0; 513 | margin-bottom: 0; 514 | } 515 | 516 | div.bodywrapper { 517 | margin-top: 0; 518 | margin-right: 0; 519 | margin-bottom: 0; 520 | margin-left: 0; 521 | } 522 | 523 | ul { 524 | margin-left: 0; 525 | } 526 | 527 | li > ul { 528 | /* Matches the 30px from the "ul, ol" selector above */ 529 | margin-left: 30px; 530 | } 531 | 532 | .document { 533 | width: auto; 534 | } 535 | 536 | .footer { 537 | width: auto; 538 | } 539 | 540 | .bodywrapper { 541 | margin: 0; 542 | } 543 | 544 | .footer { 545 | width: auto; 546 | } 547 | 548 | .github { 549 | display: none; 550 | } 551 | 552 | 553 | 554 | } 555 | 556 | 557 | 558 | @media screen and (max-width: 875px) { 559 | 560 | body { 561 | margin: 0; 562 | padding: 20px 30px; 563 | } 564 | 565 | div.documentwrapper { 566 | float: none; 567 | background: #fff; 568 | } 569 | 570 | div.sphinxsidebar { 571 | display: block; 572 | float: none; 573 | width: 102.5%; 574 | margin: 50px -30px -20px -30px; 575 | padding: 10px 20px; 576 | background: #333; 577 | color: #FFF; 578 | } 579 | 580 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 581 | div.sphinxsidebar h3 a { 582 | color: #fff; 583 | } 584 | 585 | div.sphinxsidebar a { 586 | color: #AAA; 587 | } 588 | 589 | div.sphinxsidebar p.logo { 590 | display: none; 591 | } 592 | 593 | div.document { 594 | width: 100%; 595 | margin: 0; 596 | } 597 | 598 | div.footer { 599 | display: none; 600 | } 601 | 602 | div.bodywrapper { 603 | margin: 0; 604 | } 605 | 606 | div.body { 607 | min-height: 0; 608 | padding: 0; 609 | } 610 | 611 | .rtd_doc_footer { 612 | display: none; 613 | } 614 | 615 | .document { 616 | width: auto; 617 | } 618 | 619 | .footer { 620 | width: auto; 621 | } 622 | 623 | .footer { 624 | width: auto; 625 | } 626 | 627 | .github { 628 | display: none; 629 | } 630 | } 631 | 632 | 633 | /* misc. */ 634 | 635 | .revsys-inline { 636 | display: none!important; 637 | } 638 | 639 | /* Make nested-list/multi-paragraph items look better in Releases changelog 640 | * pages. Without this, docutils' magical list fuckery causes inconsistent 641 | * formatting between different release sub-lists. 642 | */ 643 | div#changelog > div.section > ul > li > p:only-child { 644 | margin-bottom: 0; 645 | } 646 | 647 | /* Hide fugly table cell borders in ..bibliography:: directive output */ 648 | table.docutils.citation, table.docutils.citation td, table.docutils.citation th { 649 | border: none; 650 | /* Below needed in some edge cases; if not applied, bottom shadows appear */ 651 | -moz-box-shadow: none; 652 | -webkit-box-shadow: none; 653 | box-shadow: none; 654 | } 655 | 656 | 657 | /* relbar */ 658 | 659 | .related { 660 | line-height: 30px; 661 | width: 100%; 662 | font-size: 0.9rem; 663 | } 664 | 665 | .related.top { 666 | border-bottom: 1px solid #EEE; 667 | margin-bottom: 20px; 668 | } 669 | 670 | .related.bottom { 671 | border-top: 1px solid #EEE; 672 | } 673 | 674 | .related ul { 675 | padding: 0; 676 | margin: 0; 677 | list-style: none; 678 | } 679 | 680 | .related li { 681 | display: inline; 682 | } 683 | 684 | nav#rellinks { 685 | float: right; 686 | } 687 | 688 | nav#rellinks li+li:before { 689 | content: "|"; 690 | } 691 | 692 | nav#breadcrumbs li+li:before { 693 | content: "\00BB"; 694 | } 695 | 696 | /* Hide certain items when printing */ 697 | @media print { 698 | div.related { 699 | display: none; 700 | } 701 | } -------------------------------------------------------------------------------- /docs/build/html/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | div.section::after { 19 | display: block; 20 | content: ''; 21 | clear: left; 22 | } 23 | 24 | /* -- relbar ---------------------------------------------------------------- */ 25 | 26 | div.related { 27 | width: 100%; 28 | font-size: 90%; 29 | } 30 | 31 | div.related h3 { 32 | display: none; 33 | } 34 | 35 | div.related ul { 36 | margin: 0; 37 | padding: 0 0 0 10px; 38 | list-style: none; 39 | } 40 | 41 | div.related li { 42 | display: inline; 43 | } 44 | 45 | div.related li.right { 46 | float: right; 47 | margin-right: 5px; 48 | } 49 | 50 | /* -- sidebar --------------------------------------------------------------- */ 51 | 52 | div.sphinxsidebarwrapper { 53 | padding: 10px 5px 0 10px; 54 | } 55 | 56 | div.sphinxsidebar { 57 | float: left; 58 | width: 230px; 59 | margin-left: -100%; 60 | font-size: 90%; 61 | word-wrap: break-word; 62 | overflow-wrap : break-word; 63 | } 64 | 65 | div.sphinxsidebar ul { 66 | list-style: none; 67 | } 68 | 69 | div.sphinxsidebar ul ul, 70 | div.sphinxsidebar ul.want-points { 71 | margin-left: 20px; 72 | list-style: square; 73 | } 74 | 75 | div.sphinxsidebar ul ul { 76 | margin-top: 0; 77 | margin-bottom: 0; 78 | } 79 | 80 | div.sphinxsidebar form { 81 | margin-top: 10px; 82 | } 83 | 84 | div.sphinxsidebar input { 85 | border: 1px solid #98dbcc; 86 | font-family: sans-serif; 87 | font-size: 1em; 88 | } 89 | 90 | div.sphinxsidebar #searchbox form.search { 91 | overflow: hidden; 92 | } 93 | 94 | div.sphinxsidebar #searchbox input[type="text"] { 95 | float: left; 96 | width: 80%; 97 | padding: 0.25em; 98 | box-sizing: border-box; 99 | } 100 | 101 | div.sphinxsidebar #searchbox input[type="submit"] { 102 | float: left; 103 | width: 20%; 104 | border-left: none; 105 | padding: 0.25em; 106 | box-sizing: border-box; 107 | } 108 | 109 | 110 | img { 111 | border: 0; 112 | max-width: 100%; 113 | } 114 | 115 | /* -- search page ----------------------------------------------------------- */ 116 | 117 | ul.search { 118 | margin: 10px 0 0 20px; 119 | padding: 0; 120 | } 121 | 122 | ul.search li { 123 | padding: 5px 0 5px 20px; 124 | background-image: url(file.png); 125 | background-repeat: no-repeat; 126 | background-position: 0 7px; 127 | } 128 | 129 | ul.search li a { 130 | font-weight: bold; 131 | } 132 | 133 | ul.search li div.context { 134 | color: #888; 135 | margin: 2px 0 0 30px; 136 | text-align: left; 137 | } 138 | 139 | ul.keywordmatches li.goodmatch a { 140 | font-weight: bold; 141 | } 142 | 143 | /* -- index page ------------------------------------------------------------ */ 144 | 145 | table.contentstable { 146 | width: 90%; 147 | margin-left: auto; 148 | margin-right: auto; 149 | } 150 | 151 | table.contentstable p.biglink { 152 | line-height: 150%; 153 | } 154 | 155 | a.biglink { 156 | font-size: 1.3em; 157 | } 158 | 159 | span.linkdescr { 160 | font-style: italic; 161 | padding-top: 5px; 162 | font-size: 90%; 163 | } 164 | 165 | /* -- general index --------------------------------------------------------- */ 166 | 167 | table.indextable { 168 | width: 100%; 169 | } 170 | 171 | table.indextable td { 172 | text-align: left; 173 | vertical-align: top; 174 | } 175 | 176 | table.indextable ul { 177 | margin-top: 0; 178 | margin-bottom: 0; 179 | list-style-type: none; 180 | } 181 | 182 | table.indextable > tbody > tr > td > ul { 183 | padding-left: 0em; 184 | } 185 | 186 | table.indextable tr.pcap { 187 | height: 10px; 188 | } 189 | 190 | table.indextable tr.cap { 191 | margin-top: 10px; 192 | background-color: #f2f2f2; 193 | } 194 | 195 | img.toggler { 196 | margin-right: 3px; 197 | margin-top: 3px; 198 | cursor: pointer; 199 | } 200 | 201 | div.modindex-jumpbox { 202 | border-top: 1px solid #ddd; 203 | border-bottom: 1px solid #ddd; 204 | margin: 1em 0 1em 0; 205 | padding: 0.4em; 206 | } 207 | 208 | div.genindex-jumpbox { 209 | border-top: 1px solid #ddd; 210 | border-bottom: 1px solid #ddd; 211 | margin: 1em 0 1em 0; 212 | padding: 0.4em; 213 | } 214 | 215 | /* -- domain module index --------------------------------------------------- */ 216 | 217 | table.modindextable td { 218 | padding: 2px; 219 | border-collapse: collapse; 220 | } 221 | 222 | /* -- general body styles --------------------------------------------------- */ 223 | 224 | div.body { 225 | min-width: 450px; 226 | max-width: 800px; 227 | } 228 | 229 | div.body p, div.body dd, div.body li, div.body blockquote { 230 | -moz-hyphens: auto; 231 | -ms-hyphens: auto; 232 | -webkit-hyphens: auto; 233 | hyphens: auto; 234 | } 235 | 236 | a.headerlink { 237 | visibility: hidden; 238 | } 239 | 240 | a.brackets:before, 241 | span.brackets > a:before{ 242 | content: "["; 243 | } 244 | 245 | a.brackets:after, 246 | span.brackets > a:after { 247 | content: "]"; 248 | } 249 | 250 | h1:hover > a.headerlink, 251 | h2:hover > a.headerlink, 252 | h3:hover > a.headerlink, 253 | h4:hover > a.headerlink, 254 | h5:hover > a.headerlink, 255 | h6:hover > a.headerlink, 256 | dt:hover > a.headerlink, 257 | caption:hover > a.headerlink, 258 | p.caption:hover > a.headerlink, 259 | div.code-block-caption:hover > a.headerlink { 260 | visibility: visible; 261 | } 262 | 263 | div.body p.caption { 264 | text-align: inherit; 265 | } 266 | 267 | div.body td { 268 | text-align: left; 269 | } 270 | 271 | .first { 272 | margin-top: 0 !important; 273 | } 274 | 275 | p.rubric { 276 | margin-top: 30px; 277 | font-weight: bold; 278 | } 279 | 280 | img.align-left, .figure.align-left, object.align-left { 281 | clear: left; 282 | float: left; 283 | margin-right: 1em; 284 | } 285 | 286 | img.align-right, .figure.align-right, object.align-right { 287 | clear: right; 288 | float: right; 289 | margin-left: 1em; 290 | } 291 | 292 | img.align-center, .figure.align-center, object.align-center { 293 | display: block; 294 | margin-left: auto; 295 | margin-right: auto; 296 | } 297 | 298 | img.align-default, .figure.align-default { 299 | display: block; 300 | margin-left: auto; 301 | margin-right: auto; 302 | } 303 | 304 | .align-left { 305 | text-align: left; 306 | } 307 | 308 | .align-center { 309 | text-align: center; 310 | } 311 | 312 | .align-default { 313 | text-align: center; 314 | } 315 | 316 | .align-right { 317 | text-align: right; 318 | } 319 | 320 | /* -- sidebars -------------------------------------------------------------- */ 321 | 322 | div.sidebar { 323 | margin: 0 0 0.5em 1em; 324 | border: 1px solid #ddb; 325 | padding: 7px; 326 | background-color: #ffe; 327 | width: 40%; 328 | float: right; 329 | clear: right; 330 | overflow-x: auto; 331 | } 332 | 333 | p.sidebar-title { 334 | font-weight: bold; 335 | } 336 | 337 | div.admonition, div.topic, blockquote { 338 | clear: left; 339 | } 340 | 341 | /* -- topics ---------------------------------------------------------------- */ 342 | 343 | div.topic { 344 | border: 1px solid #ccc; 345 | padding: 7px; 346 | margin: 10px 0 10px 0; 347 | } 348 | 349 | p.topic-title { 350 | font-size: 1.1em; 351 | font-weight: bold; 352 | margin-top: 10px; 353 | } 354 | 355 | /* -- admonitions ----------------------------------------------------------- */ 356 | 357 | div.admonition { 358 | margin-top: 10px; 359 | margin-bottom: 10px; 360 | padding: 7px; 361 | } 362 | 363 | div.admonition dt { 364 | font-weight: bold; 365 | } 366 | 367 | p.admonition-title { 368 | margin: 0px 10px 5px 0px; 369 | font-weight: bold; 370 | } 371 | 372 | div.body p.centered { 373 | text-align: center; 374 | margin-top: 25px; 375 | } 376 | 377 | /* -- content of sidebars/topics/admonitions -------------------------------- */ 378 | 379 | div.sidebar > :last-child, 380 | div.topic > :last-child, 381 | div.admonition > :last-child { 382 | margin-bottom: 0; 383 | } 384 | 385 | div.sidebar::after, 386 | div.topic::after, 387 | div.admonition::after, 388 | blockquote::after { 389 | display: block; 390 | content: ''; 391 | clear: both; 392 | } 393 | 394 | /* -- tables ---------------------------------------------------------------- */ 395 | 396 | table.docutils { 397 | margin-top: 10px; 398 | margin-bottom: 10px; 399 | border: 0; 400 | border-collapse: collapse; 401 | } 402 | 403 | table.align-center { 404 | margin-left: auto; 405 | margin-right: auto; 406 | } 407 | 408 | table.align-default { 409 | margin-left: auto; 410 | margin-right: auto; 411 | } 412 | 413 | table caption span.caption-number { 414 | font-style: italic; 415 | } 416 | 417 | table caption span.caption-text { 418 | } 419 | 420 | table.docutils td, table.docutils th { 421 | padding: 1px 8px 1px 5px; 422 | border-top: 0; 423 | border-left: 0; 424 | border-right: 0; 425 | border-bottom: 1px solid #aaa; 426 | } 427 | 428 | table.footnote td, table.footnote th { 429 | border: 0 !important; 430 | } 431 | 432 | th { 433 | text-align: left; 434 | padding-right: 5px; 435 | } 436 | 437 | table.citation { 438 | border-left: solid 1px gray; 439 | margin-left: 1px; 440 | } 441 | 442 | table.citation td { 443 | border-bottom: none; 444 | } 445 | 446 | th > :first-child, 447 | td > :first-child { 448 | margin-top: 0px; 449 | } 450 | 451 | th > :last-child, 452 | td > :last-child { 453 | margin-bottom: 0px; 454 | } 455 | 456 | /* -- figures --------------------------------------------------------------- */ 457 | 458 | div.figure { 459 | margin: 0.5em; 460 | padding: 0.5em; 461 | } 462 | 463 | div.figure p.caption { 464 | padding: 0.3em; 465 | } 466 | 467 | div.figure p.caption span.caption-number { 468 | font-style: italic; 469 | } 470 | 471 | div.figure p.caption span.caption-text { 472 | } 473 | 474 | /* -- field list styles ----------------------------------------------------- */ 475 | 476 | table.field-list td, table.field-list th { 477 | border: 0 !important; 478 | } 479 | 480 | .field-list ul { 481 | margin: 0; 482 | padding-left: 1em; 483 | } 484 | 485 | .field-list p { 486 | margin: 0; 487 | } 488 | 489 | .field-name { 490 | -moz-hyphens: manual; 491 | -ms-hyphens: manual; 492 | -webkit-hyphens: manual; 493 | hyphens: manual; 494 | } 495 | 496 | /* -- hlist styles ---------------------------------------------------------- */ 497 | 498 | table.hlist { 499 | margin: 1em 0; 500 | } 501 | 502 | table.hlist td { 503 | vertical-align: top; 504 | } 505 | 506 | 507 | /* -- other body styles ----------------------------------------------------- */ 508 | 509 | ol.arabic { 510 | list-style: decimal; 511 | } 512 | 513 | ol.loweralpha { 514 | list-style: lower-alpha; 515 | } 516 | 517 | ol.upperalpha { 518 | list-style: upper-alpha; 519 | } 520 | 521 | ol.lowerroman { 522 | list-style: lower-roman; 523 | } 524 | 525 | ol.upperroman { 526 | list-style: upper-roman; 527 | } 528 | 529 | :not(li) > ol > li:first-child > :first-child, 530 | :not(li) > ul > li:first-child > :first-child { 531 | margin-top: 0px; 532 | } 533 | 534 | :not(li) > ol > li:last-child > :last-child, 535 | :not(li) > ul > li:last-child > :last-child { 536 | margin-bottom: 0px; 537 | } 538 | 539 | ol.simple ol p, 540 | ol.simple ul p, 541 | ul.simple ol p, 542 | ul.simple ul p { 543 | margin-top: 0; 544 | } 545 | 546 | ol.simple > li:not(:first-child) > p, 547 | ul.simple > li:not(:first-child) > p { 548 | margin-top: 0; 549 | } 550 | 551 | ol.simple p, 552 | ul.simple p { 553 | margin-bottom: 0; 554 | } 555 | 556 | dl.footnote > dt, 557 | dl.citation > dt { 558 | float: left; 559 | margin-right: 0.5em; 560 | } 561 | 562 | dl.footnote > dd, 563 | dl.citation > dd { 564 | margin-bottom: 0em; 565 | } 566 | 567 | dl.footnote > dd:after, 568 | dl.citation > dd:after { 569 | content: ""; 570 | clear: both; 571 | } 572 | 573 | dl.field-list { 574 | display: grid; 575 | grid-template-columns: fit-content(30%) auto; 576 | } 577 | 578 | dl.field-list > dt { 579 | font-weight: bold; 580 | word-break: break-word; 581 | padding-left: 0.5em; 582 | padding-right: 5px; 583 | } 584 | 585 | dl.field-list > dt:after { 586 | content: ":"; 587 | } 588 | 589 | dl.field-list > dd { 590 | padding-left: 0.5em; 591 | margin-top: 0em; 592 | margin-left: 0em; 593 | margin-bottom: 0em; 594 | } 595 | 596 | dl { 597 | margin-bottom: 15px; 598 | } 599 | 600 | dd > :first-child { 601 | margin-top: 0px; 602 | } 603 | 604 | dd ul, dd table { 605 | margin-bottom: 10px; 606 | } 607 | 608 | dd { 609 | margin-top: 3px; 610 | margin-bottom: 10px; 611 | margin-left: 30px; 612 | } 613 | 614 | dl > dd:last-child, 615 | dl > dd:last-child > :last-child { 616 | margin-bottom: 0; 617 | } 618 | 619 | dt:target, span.highlighted { 620 | background-color: #fbe54e; 621 | } 622 | 623 | rect.highlighted { 624 | fill: #fbe54e; 625 | } 626 | 627 | dl.glossary dt { 628 | font-weight: bold; 629 | font-size: 1.1em; 630 | } 631 | 632 | .optional { 633 | font-size: 1.3em; 634 | } 635 | 636 | .sig-paren { 637 | font-size: larger; 638 | } 639 | 640 | .versionmodified { 641 | font-style: italic; 642 | } 643 | 644 | .system-message { 645 | background-color: #fda; 646 | padding: 5px; 647 | border: 3px solid red; 648 | } 649 | 650 | .footnote:target { 651 | background-color: #ffa; 652 | } 653 | 654 | .line-block { 655 | display: block; 656 | margin-top: 1em; 657 | margin-bottom: 1em; 658 | } 659 | 660 | .line-block .line-block { 661 | margin-top: 0; 662 | margin-bottom: 0; 663 | margin-left: 1.5em; 664 | } 665 | 666 | .guilabel, .menuselection { 667 | font-family: sans-serif; 668 | } 669 | 670 | .accelerator { 671 | text-decoration: underline; 672 | } 673 | 674 | .classifier { 675 | font-style: oblique; 676 | } 677 | 678 | .classifier:before { 679 | font-style: normal; 680 | margin: 0.5em; 681 | content: ":"; 682 | } 683 | 684 | abbr, acronym { 685 | border-bottom: dotted 1px; 686 | cursor: help; 687 | } 688 | 689 | /* -- code displays --------------------------------------------------------- */ 690 | 691 | pre { 692 | overflow: auto; 693 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 694 | } 695 | 696 | pre, div[class*="highlight-"] { 697 | clear: both; 698 | } 699 | 700 | span.pre { 701 | -moz-hyphens: none; 702 | -ms-hyphens: none; 703 | -webkit-hyphens: none; 704 | hyphens: none; 705 | } 706 | 707 | div[class*="highlight-"] { 708 | margin: 1em 0; 709 | } 710 | 711 | td.linenos pre { 712 | border: 0; 713 | background-color: transparent; 714 | color: #aaa; 715 | } 716 | 717 | table.highlighttable { 718 | display: block; 719 | } 720 | 721 | table.highlighttable tbody { 722 | display: block; 723 | } 724 | 725 | table.highlighttable tr { 726 | display: flex; 727 | } 728 | 729 | table.highlighttable td { 730 | margin: 0; 731 | padding: 0; 732 | } 733 | 734 | table.highlighttable td.linenos { 735 | padding-right: 0.5em; 736 | } 737 | 738 | table.highlighttable td.code { 739 | flex: 1; 740 | overflow: hidden; 741 | } 742 | 743 | .highlight .hll { 744 | display: block; 745 | } 746 | 747 | div.highlight pre, 748 | table.highlighttable pre { 749 | margin: 0; 750 | } 751 | 752 | div.code-block-caption + div { 753 | margin-top: 0; 754 | } 755 | 756 | div.code-block-caption { 757 | margin-top: 1em; 758 | padding: 2px 5px; 759 | font-size: small; 760 | } 761 | 762 | div.code-block-caption code { 763 | background-color: transparent; 764 | } 765 | 766 | table.highlighttable td.linenos, 767 | div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ 768 | user-select: none; 769 | } 770 | 771 | div.code-block-caption span.caption-number { 772 | padding: 0.1em 0.3em; 773 | font-style: italic; 774 | } 775 | 776 | div.code-block-caption span.caption-text { 777 | } 778 | 779 | div.literal-block-wrapper { 780 | margin: 1em 0; 781 | } 782 | 783 | code.descname { 784 | background-color: transparent; 785 | font-weight: bold; 786 | font-size: 1.2em; 787 | } 788 | 789 | code.descclassname { 790 | background-color: transparent; 791 | } 792 | 793 | code.xref, a code { 794 | background-color: transparent; 795 | font-weight: bold; 796 | } 797 | 798 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 799 | background-color: transparent; 800 | } 801 | 802 | .viewcode-link { 803 | float: right; 804 | } 805 | 806 | .viewcode-back { 807 | float: right; 808 | font-family: sans-serif; 809 | } 810 | 811 | div.viewcode-block:target { 812 | margin: -1px -10px; 813 | padding: 0 10px; 814 | } 815 | 816 | /* -- math display ---------------------------------------------------------- */ 817 | 818 | img.math { 819 | vertical-align: middle; 820 | } 821 | 822 | div.body div.math p { 823 | text-align: center; 824 | } 825 | 826 | span.eqno { 827 | float: right; 828 | } 829 | 830 | span.eqno a.headerlink { 831 | position: absolute; 832 | z-index: 1; 833 | } 834 | 835 | div.math:hover a.headerlink { 836 | visibility: visible; 837 | } 838 | 839 | /* -- printout stylesheet --------------------------------------------------- */ 840 | 841 | @media print { 842 | div.document, 843 | div.documentwrapper, 844 | div.bodywrapper { 845 | margin: 0 !important; 846 | width: 100%; 847 | } 848 | 849 | div.sphinxsidebar, 850 | div.related, 851 | div.footer, 852 | #top-link { 853 | display: none; 854 | } 855 | } -------------------------------------------------------------------------------- /docs/build/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | */ 33 | jQuery.urldecode = function(x) { 34 | return decodeURIComponent(x).replace(/\+/g, ' '); 35 | }; 36 | 37 | /** 38 | * small helper function to urlencode strings 39 | */ 40 | jQuery.urlencode = encodeURIComponent; 41 | 42 | /** 43 | * This function returns the parsed url parameters of the 44 | * current request. Multiple values per key are supported, 45 | * it will always return arrays of strings for the value parts. 46 | */ 47 | jQuery.getQueryParameters = function(s) { 48 | if (typeof s === 'undefined') 49 | s = document.location.search; 50 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 51 | var result = {}; 52 | for (var i = 0; i < parts.length; i++) { 53 | var tmp = parts[i].split('=', 2); 54 | var key = jQuery.urldecode(tmp[0]); 55 | var value = jQuery.urldecode(tmp[1]); 56 | if (key in result) 57 | result[key].push(value); 58 | else 59 | result[key] = [value]; 60 | } 61 | return result; 62 | }; 63 | 64 | /** 65 | * highlight a given string on a jquery object by wrapping it in 66 | * span elements with the given class name. 67 | */ 68 | jQuery.fn.highlightText = function(text, className) { 69 | function highlight(node, addItems) { 70 | if (node.nodeType === 3) { 71 | var val = node.nodeValue; 72 | var pos = val.toLowerCase().indexOf(text); 73 | if (pos >= 0 && 74 | !jQuery(node.parentNode).hasClass(className) && 75 | !jQuery(node.parentNode).hasClass("nohighlight")) { 76 | var span; 77 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 78 | if (isInSVG) { 79 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 80 | } else { 81 | span = document.createElement("span"); 82 | span.className = className; 83 | } 84 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 85 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 86 | document.createTextNode(val.substr(pos + text.length)), 87 | node.nextSibling)); 88 | node.nodeValue = val.substr(0, pos); 89 | if (isInSVG) { 90 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 91 | var bbox = node.parentElement.getBBox(); 92 | rect.x.baseVal.value = bbox.x; 93 | rect.y.baseVal.value = bbox.y; 94 | rect.width.baseVal.value = bbox.width; 95 | rect.height.baseVal.value = bbox.height; 96 | rect.setAttribute('class', className); 97 | addItems.push({ 98 | "parent": node.parentNode, 99 | "target": rect}); 100 | } 101 | } 102 | } 103 | else if (!jQuery(node).is("button, select, textarea")) { 104 | jQuery.each(node.childNodes, function() { 105 | highlight(this, addItems); 106 | }); 107 | } 108 | } 109 | var addItems = []; 110 | var result = this.each(function() { 111 | highlight(this, addItems); 112 | }); 113 | for (var i = 0; i < addItems.length; ++i) { 114 | jQuery(addItems[i].parent).before(addItems[i].target); 115 | } 116 | return result; 117 | }; 118 | 119 | /* 120 | * backward compatibility for jQuery.browser 121 | * This will be supported until firefox bug is fixed. 122 | */ 123 | if (!jQuery.browser) { 124 | jQuery.uaMatch = function(ua) { 125 | ua = ua.toLowerCase(); 126 | 127 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 128 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 129 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 130 | /(msie) ([\w.]+)/.exec(ua) || 131 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 132 | []; 133 | 134 | return { 135 | browser: match[ 1 ] || "", 136 | version: match[ 2 ] || "0" 137 | }; 138 | }; 139 | jQuery.browser = {}; 140 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 141 | } 142 | 143 | /** 144 | * Small JavaScript module for the documentation. 145 | */ 146 | var Documentation = { 147 | 148 | init : function() { 149 | this.fixFirefoxAnchorBug(); 150 | this.highlightSearchWords(); 151 | this.initIndexTable(); 152 | if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { 153 | this.initOnKeyListeners(); 154 | } 155 | }, 156 | 157 | /** 158 | * i18n support 159 | */ 160 | TRANSLATIONS : {}, 161 | PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, 162 | LOCALE : 'unknown', 163 | 164 | // gettext and ngettext don't access this so that the functions 165 | // can safely bound to a different name (_ = Documentation.gettext) 166 | gettext : function(string) { 167 | var translated = Documentation.TRANSLATIONS[string]; 168 | if (typeof translated === 'undefined') 169 | return string; 170 | return (typeof translated === 'string') ? translated : translated[0]; 171 | }, 172 | 173 | ngettext : function(singular, plural, n) { 174 | var translated = Documentation.TRANSLATIONS[singular]; 175 | if (typeof translated === 'undefined') 176 | return (n == 1) ? singular : plural; 177 | return translated[Documentation.PLURALEXPR(n)]; 178 | }, 179 | 180 | addTranslations : function(catalog) { 181 | for (var key in catalog.messages) 182 | this.TRANSLATIONS[key] = catalog.messages[key]; 183 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 184 | this.LOCALE = catalog.locale; 185 | }, 186 | 187 | /** 188 | * add context elements like header anchor links 189 | */ 190 | addContextElements : function() { 191 | $('div[id] > :header:first').each(function() { 192 | $('\u00B6'). 193 | attr('href', '#' + this.id). 194 | attr('title', _('Permalink to this headline')). 195 | appendTo(this); 196 | }); 197 | $('dt[id]').each(function() { 198 | $('\u00B6'). 199 | attr('href', '#' + this.id). 200 | attr('title', _('Permalink to this definition')). 201 | appendTo(this); 202 | }); 203 | }, 204 | 205 | /** 206 | * workaround a firefox stupidity 207 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 208 | */ 209 | fixFirefoxAnchorBug : function() { 210 | if (document.location.hash && $.browser.mozilla) 211 | window.setTimeout(function() { 212 | document.location.href += ''; 213 | }, 10); 214 | }, 215 | 216 | /** 217 | * highlight the search words provided in the url in the text 218 | */ 219 | highlightSearchWords : function() { 220 | var params = $.getQueryParameters(); 221 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 222 | if (terms.length) { 223 | var body = $('div.body'); 224 | if (!body.length) { 225 | body = $('body'); 226 | } 227 | window.setTimeout(function() { 228 | $.each(terms, function() { 229 | body.highlightText(this.toLowerCase(), 'highlighted'); 230 | }); 231 | }, 10); 232 | $('') 234 | .appendTo($('#searchbox')); 235 | } 236 | }, 237 | 238 | /** 239 | * init the domain index toggle buttons 240 | */ 241 | initIndexTable : function() { 242 | var togglers = $('img.toggler').click(function() { 243 | var src = $(this).attr('src'); 244 | var idnum = $(this).attr('id').substr(7); 245 | $('tr.cg-' + idnum).toggle(); 246 | if (src.substr(-9) === 'minus.png') 247 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 248 | else 249 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 250 | }).css('display', ''); 251 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 252 | togglers.click(); 253 | } 254 | }, 255 | 256 | /** 257 | * helper function to hide the search marks again 258 | */ 259 | hideSearchWords : function() { 260 | $('#searchbox .highlight-link').fadeOut(300); 261 | $('span.highlighted').removeClass('highlighted'); 262 | }, 263 | 264 | /** 265 | * make the url absolute 266 | */ 267 | makeURL : function(relativeURL) { 268 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 269 | }, 270 | 271 | /** 272 | * get the current relative url 273 | */ 274 | getCurrentURL : function() { 275 | var path = document.location.pathname; 276 | var parts = path.split(/\//); 277 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 278 | if (this === '..') 279 | parts.pop(); 280 | }); 281 | var url = parts.join('/'); 282 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 283 | }, 284 | 285 | initOnKeyListeners: function() { 286 | $(document).keydown(function(event) { 287 | var activeElementType = document.activeElement.tagName; 288 | // don't navigate when in search box or textarea 289 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' 290 | && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) { 291 | switch (event.keyCode) { 292 | case 37: // left 293 | var prevHref = $('link[rel="prev"]').prop('href'); 294 | if (prevHref) { 295 | window.location.href = prevHref; 296 | return false; 297 | } 298 | case 39: // right 299 | var nextHref = $('link[rel="next"]').prop('href'); 300 | if (nextHref) { 301 | window.location.href = nextHref; 302 | return false; 303 | } 304 | } 305 | } 306 | }); 307 | } 308 | }; 309 | 310 | // quick alias for translations 311 | _ = Documentation.gettext; 312 | 313 | $(document).ready(function() { 314 | Documentation.init(); 315 | }); 316 | -------------------------------------------------------------------------------- /docs/build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '1.0.0', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false 12 | }; -------------------------------------------------------------------------------- /docs/build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/file.png -------------------------------------------------------------------------------- /docs/build/html/_static/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); -------------------------------------------------------------------------------- /docs/build/html/_static/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/build/html/_static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/build/html/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}t.length>0&&($(".wy-menu-vertical .current").removeClass("current"),t.addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l1").parent().addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l2").addClass("current"),t.closest("li.toctree-l3").addClass("current"),t.closest("li.toctree-l4").addClass("current"),t.closest("li.toctree-l5").addClass("current"),t[0].scrollIntoView())}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 62 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 63 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 64 | var s_v = "^(" + C + ")?" + v; // vowel in stem 65 | 66 | this.stemWord = function (w) { 67 | var stem; 68 | var suffix; 69 | var firstch; 70 | var origword = w; 71 | 72 | if (w.length < 3) 73 | return w; 74 | 75 | var re; 76 | var re2; 77 | var re3; 78 | var re4; 79 | 80 | firstch = w.substr(0,1); 81 | if (firstch == "y") 82 | w = firstch.toUpperCase() + w.substr(1); 83 | 84 | // Step 1a 85 | re = /^(.+?)(ss|i)es$/; 86 | re2 = /^(.+?)([^s])s$/; 87 | 88 | if (re.test(w)) 89 | w = w.replace(re,"$1$2"); 90 | else if (re2.test(w)) 91 | w = w.replace(re2,"$1$2"); 92 | 93 | // Step 1b 94 | re = /^(.+?)eed$/; 95 | re2 = /^(.+?)(ed|ing)$/; 96 | if (re.test(w)) { 97 | var fp = re.exec(w); 98 | re = new RegExp(mgr0); 99 | if (re.test(fp[1])) { 100 | re = /.$/; 101 | w = w.replace(re,""); 102 | } 103 | } 104 | else if (re2.test(w)) { 105 | var fp = re2.exec(w); 106 | stem = fp[1]; 107 | re2 = new RegExp(s_v); 108 | if (re2.test(stem)) { 109 | w = stem; 110 | re2 = /(at|bl|iz)$/; 111 | re3 = new RegExp("([^aeiouylsz])\\1$"); 112 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 113 | if (re2.test(w)) 114 | w = w + "e"; 115 | else if (re3.test(w)) { 116 | re = /.$/; 117 | w = w.replace(re,""); 118 | } 119 | else if (re4.test(w)) 120 | w = w + "e"; 121 | } 122 | } 123 | 124 | // Step 1c 125 | re = /^(.+?)y$/; 126 | if (re.test(w)) { 127 | var fp = re.exec(w); 128 | stem = fp[1]; 129 | re = new RegExp(s_v); 130 | if (re.test(stem)) 131 | w = stem + "i"; 132 | } 133 | 134 | // Step 2 135 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 136 | if (re.test(w)) { 137 | var fp = re.exec(w); 138 | stem = fp[1]; 139 | suffix = fp[2]; 140 | re = new RegExp(mgr0); 141 | if (re.test(stem)) 142 | w = stem + step2list[suffix]; 143 | } 144 | 145 | // Step 3 146 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 147 | if (re.test(w)) { 148 | var fp = re.exec(w); 149 | stem = fp[1]; 150 | suffix = fp[2]; 151 | re = new RegExp(mgr0); 152 | if (re.test(stem)) 153 | w = stem + step3list[suffix]; 154 | } 155 | 156 | // Step 4 157 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 158 | re2 = /^(.+?)(s|t)(ion)$/; 159 | if (re.test(w)) { 160 | var fp = re.exec(w); 161 | stem = fp[1]; 162 | re = new RegExp(mgr1); 163 | if (re.test(stem)) 164 | w = stem; 165 | } 166 | else if (re2.test(w)) { 167 | var fp = re2.exec(w); 168 | stem = fp[1] + fp[2]; 169 | re2 = new RegExp(mgr1); 170 | if (re2.test(stem)) 171 | w = stem; 172 | } 173 | 174 | // Step 5 175 | re = /^(.+?)e$/; 176 | if (re.test(w)) { 177 | var fp = re.exec(w); 178 | stem = fp[1]; 179 | re = new RegExp(mgr1); 180 | re2 = new RegExp(meq1); 181 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 182 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 183 | w = stem; 184 | } 185 | re = /ll$/; 186 | re2 = new RegExp(mgr1); 187 | if (re.test(w) && re2.test(w)) { 188 | re = /.$/; 189 | w = w.replace(re,""); 190 | } 191 | 192 | // and turn initial Y back to y 193 | if (firstch == "y") 194 | w = firstch.toLowerCase() + w.substr(1); 195 | return w; 196 | } 197 | } 198 | 199 | 200 | 201 | 202 | 203 | var splitChars = (function() { 204 | var result = {}; 205 | var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, 206 | 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, 207 | 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, 208 | 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, 209 | 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, 210 | 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, 211 | 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, 212 | 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, 213 | 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, 214 | 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; 215 | var i, j, start, end; 216 | for (i = 0; i < singles.length; i++) { 217 | result[singles[i]] = true; 218 | } 219 | var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], 220 | [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], 221 | [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], 222 | [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], 223 | [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], 224 | [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], 225 | [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], 226 | [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], 227 | [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], 228 | [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], 229 | [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], 230 | [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], 231 | [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], 232 | [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], 233 | [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], 234 | [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], 235 | [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], 236 | [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], 237 | [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], 238 | [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], 239 | [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], 240 | [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], 241 | [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], 242 | [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], 243 | [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], 244 | [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], 245 | [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], 246 | [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], 247 | [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], 248 | [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], 249 | [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], 250 | [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], 251 | [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], 252 | [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], 253 | [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], 254 | [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], 255 | [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], 256 | [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], 257 | [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], 258 | [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], 259 | [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], 260 | [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], 261 | [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], 262 | [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], 263 | [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], 264 | [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], 265 | [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], 266 | [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], 267 | [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; 268 | for (i = 0; i < ranges.length; i++) { 269 | start = ranges[i][0]; 270 | end = ranges[i][1]; 271 | for (j = start; j <= end; j++) { 272 | result[j] = true; 273 | } 274 | } 275 | return result; 276 | })(); 277 | 278 | function splitQuery(query) { 279 | var result = []; 280 | var start = -1; 281 | for (var i = 0; i < query.length; i++) { 282 | if (splitChars[query.charCodeAt(i)]) { 283 | if (start !== -1) { 284 | result.push(query.slice(start, i)); 285 | start = -1; 286 | } 287 | } else if (start === -1) { 288 | start = i; 289 | } 290 | } 291 | if (start !== -1) { 292 | result.push(query.slice(start)); 293 | } 294 | return result; 295 | } 296 | 297 | 298 | -------------------------------------------------------------------------------- /docs/build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; margin: 0; } 2 | td.linenos pre { color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; } 4 | td.linenos pre.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #408080; font-style: italic } /* Comment */ 9 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 10 | .highlight .k { color: #008000; font-weight: bold } /* Keyword */ 11 | .highlight .o { color: #666666 } /* Operator */ 12 | .highlight .ch { color: #408080; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #BC7A00 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ 18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 19 | .highlight .ge { font-style: italic } /* Generic.Emph */ 20 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 23 | .highlight .go { color: #888888 } /* Generic.Output */ 24 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 25 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 28 | .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 29 | .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 30 | .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 31 | .highlight .kp { color: #008000 } /* Keyword.Pseudo */ 32 | .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 33 | .highlight .kt { color: #B00040 } /* Keyword.Type */ 34 | .highlight .m { color: #666666 } /* Literal.Number */ 35 | .highlight .s { color: #BA2121 } /* Literal.String */ 36 | .highlight .na { color: #7D9029 } /* Name.Attribute */ 37 | .highlight .nb { color: #008000 } /* Name.Builtin */ 38 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 39 | .highlight .no { color: #880000 } /* Name.Constant */ 40 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 41 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 42 | .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 43 | .highlight .nf { color: #0000FF } /* Name.Function */ 44 | .highlight .nl { color: #A0A000 } /* Name.Label */ 45 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 46 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ 47 | .highlight .nv { color: #19177C } /* Name.Variable */ 48 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 49 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 50 | .highlight .mb { color: #666666 } /* Literal.Number.Bin */ 51 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 52 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 53 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 54 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 55 | .highlight .sa { color: #BA2121 } /* Literal.String.Affix */ 56 | .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ 57 | .highlight .sc { color: #BA2121 } /* Literal.String.Char */ 58 | .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ 59 | .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 60 | .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ 61 | .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 62 | .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ 63 | .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 64 | .highlight .sx { color: #008000 } /* Literal.String.Other */ 65 | .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ 66 | .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ 67 | .highlight .ss { color: #19177C } /* Literal.String.Symbol */ 68 | .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ 69 | .highlight .fm { color: #0000FF } /* Name.Function.Magic */ 70 | .highlight .vc { color: #19177C } /* Name.Variable.Class */ 71 | .highlight .vg { color: #19177C } /* Name.Variable.Global */ 72 | .highlight .vi { color: #19177C } /* Name.Variable.Instance */ 73 | .highlight .vm { color: #19177C } /* Name.Variable.Magic */ 74 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/build/html/_static/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.3.1 2 | // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== 9 | c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c, 10 | h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each= 11 | b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a== 12 | null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= 13 | function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= 14 | e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= 15 | function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, 17 | c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}}; 24 | b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, 25 | 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; 26 | b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; 27 | b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), 28 | function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ 29 | u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= 30 | function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= 31 | true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); 32 | -------------------------------------------------------------------------------- /docs/build/html/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Index — getgfs 1.0.0 documentation 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | 97 | 98 |
99 | 100 | 101 | 107 | 108 | 109 |
110 | 111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
132 | 133 |
    134 | 135 |
  • »
  • 136 | 137 |
  • Index
  • 138 | 139 | 140 |
  • 141 | 142 | 143 | 144 |
  • 145 | 146 |
147 | 148 | 149 |
150 |
151 |
152 |
153 | 154 | 155 |

Index

156 | 157 |
158 | C 159 | | D 160 | | E 161 | | F 162 | | G 163 | | H 164 | | M 165 | | R 166 | | S 167 | | T 168 | | V 169 | 170 |
171 |

C

172 | 173 | 177 |
178 | 179 |

D

180 | 181 | 185 | 189 |
190 | 191 |

E

192 | 193 | 197 |
198 | 199 |

F

200 | 201 | 205 | 209 |
210 | 211 |

G

212 | 213 | 228 | 251 |
    229 |
  • 230 | getgfs.decode 231 | 232 |
  • 236 |
  • 237 | getgfs.getgfs 238 | 239 |
  • 243 |
  • 244 | getgfs.test 245 | 246 |
  • 250 |
252 | 253 |

H

254 | 255 | 259 |
260 | 261 |

M

262 | 263 | 278 |
279 | 280 |

R

281 | 282 | 286 |
287 | 288 |

S

289 | 290 | 294 |
295 | 296 |

T

297 | 298 | 304 | 312 |
313 | 314 |

V

315 | 316 | 320 | 326 |
327 | 328 | 329 | 330 |
331 | 332 |
333 |
334 | 335 |
336 | 337 |
338 |

339 | © Copyright 2021, Jago Strong-Wright. 340 | 341 |

342 |
343 | 344 | 345 | 346 | Built with Sphinx using a 347 | 348 | theme 349 | 350 | provided by Read the Docs. 351 | 352 |
353 |
354 |
355 | 356 |
357 | 358 |
359 | 360 | 361 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /docs/build/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Welcome to getgfs — getgfs 1.0.0 documentation 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 | 98 | 99 |
100 | 101 | 102 | 108 | 109 | 110 |
111 | 112 |
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
133 | 134 |
    135 | 136 |
  • »
  • 137 | 138 |
  • Welcome to getgfs
  • 139 | 140 | 141 |
  • 142 | 143 | 144 | View page source 145 | 146 | 147 |
  • 148 | 149 |
150 | 151 | 152 |
153 |
154 |
155 |
156 | 157 |
158 |

Welcome to getgfs

159 |

getgfs extracts weather forecast variables from the NOAA GFS forecast in a pure python, no obscure dependencies way. Currently you can:

160 |
    161 |
  • “Connect” to a forecast

  • 162 |
  • Search the variables

  • 163 |
  • Download variables for any time range, longitude, latitude and altitude

  • 164 |
  • Download “wind profiles” where you get an interpolation object for the u and v wind components by altitude

  • 165 |
166 |
167 |
168 |

Installing

169 |

Installation is simple with PyPi:

170 |

`pip install getgfs`

171 |
172 |
173 |

Usage

174 |

The library is straight forward to use. To get started create a Forecast object by:

175 |
1
176 | 2
>>>import getgfs
177 | >>>f=getgfs.Forecast("0p25")
178 | 
179 |
180 |

You can chose the resolution to be 0p25, 0p50 or 1p00 and for the 0p25 forecast you can optional specify a shorter timestep by adding 1hr after.

181 |

First to find what variable you are looking for use the search function, for example if I want the wind speed I could search for “wind”:

182 |
1
183 | 2
>>>f.search("wind")
184 | [('gustsfc', '** surface wind speed (gust) [m/s] ', 100), ('ugrdprs', '** (1000 975 950 925 900.. 7 5 3 2 1) u-component of wind [m/s] ', 125), ('ugrd_1829m', '** 1829 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd_2743m', '** 2743 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd_3658m', '** 3658 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd10m', '** 10 m above ground u-component of wind [m/s] ', 125), ('ugrd20m', '** 20 m above ground u-component of wind [m/s] ', 125), ('ugrd30m', '** 30 m above ground u-component of wind [m/s] ', 125), ('ugrd40m', '** 40 m above ground u-component of wind [m/s] ', 125), ('ugrd50m', '** 50 m above ground u-component of wind [m/s] ', 125), ('ugrd80m', '** 80 m above ground u-component of wind [m/s] ', 125), ('ugrd100m', '** 100 m above ground u-component of wind [m/s] ', 125), ('ugrdsig995', '** 0.995 sigma level u-component of wind [m/s] ', 125), ('ugrd30_0mb', '** 30-0 mb above ground u-component of wind [m/s] ', 125), ('ugrd2pv', '** pv=2e-06 (km^2/kg/s) surface u-component of wind [m/s] ', 125), ('ugrdneg2pv', '** pv=-2e-06 (km^2/kg/s) surface u-component of wind [m/s] ', 125), ('ugrdpbl', '** planetary boundary layer u-component of wind [m/s] ', 125), ('ugrdtrop', '** tropopause u-component of wind [m/s] ', 125), ('vgrdprs', '** (1000 975 950 925 900.. 7 5 3 2 1) v-component of wind [m/s] ', 125), ('vgrd_1829m', '** 1829 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd_2743m', '** 2743 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd_3658m', '** 3658 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd10m', '** 10 m above ground v-component of wind [m/s] ', 125), ('vgrd20m', '** 20 m above ground v-component of wind [m/s] ', 125), ('vgrd30m', '** 30 m above ground v-component of wind [m/s] ', 125), ('vgrd40m', '** 40 m above ground v-component of wind [m/s] ', 125), ('vgrd50m', '** 50 m above ground v-component of wind [m/s] ', 125), ('vgrd80m', '** 80 m above ground v-component of wind [m/s] ', 125), ('vgrd100m', '** 100 m above ground v-component of wind [m/s] ', 125), ('vgrdsig995', '** 0.995 sigma level v-component of wind [m/s] ', 125), ('vgrd30_0mb', '** 30-0 mb above ground v-component of wind [m/s] ', 125), ('vgrd2pv', '** pv=2e-06 (km^2/kg/s) surface v-component of wind [m/s] ', 125), ('vgrdneg2pv', '** pv=-2e-06 (km^2/kg/s) surface v-component of wind [m/s] ', 125), ('vgrdpbl', '** planetary boundary layer v-component of wind [m/s] ', 125), ('vgrdtrop', '** tropopause v-component of wind [m/s] ', 125), ('hgtmwl', '** max wind geopotential height [gpm] ', 133), ('icahtmwl', '** max wind icao standard atmosphere reference height [m] ', 133), ('presmwl', '** max wind pressure [pa] ', 133), ('tmpmwl', '** max wind temperature [k] ', 133), ('ugrdmwl', '** max wind u-component of wind [m/s] ', 133), ('vgrdmwl', '** max wind v-component of wind [m/s] ', 133)]
185 | 
186 |
187 |

So now I can see I might want “gustsfc”. Now if I want the wind speed at N70.1 W94.7 at 5:30 on the 27th of Febuary (only forecasts going back around a week are available and furutre times available depend on the forecast - look for f.times) I could do:

188 |
1
189 | 2
190 | 3
>>>res=f.get(["gustsfc"],"20210227 5:30", 70.1,-94.7)
191 | >>>res.variables["gustsfc"].data
192 | array([[[18.808477]]])
193 | 
194 |
195 |

You can get more information (e.g. what is the units of this) by exploring the variables information

196 |
1
197 | 2
>>>f.variables["gustsfc"]
198 | {'_FillValue': 9.999e+20, 'missing_value': 9.999e+20, 'long_name': '** surface wind speed (gust) [m/s] ', 'level_dependent': False}
199 | 
200 |
201 |

You can also get multiple variables by including more names in the list or a range of positions by using “’[min_lat:max_lat]’” type strings in place of the position parameters.

202 |
203 |
204 |

About

205 |

The incentive to write this library was that the current method to get any variable was to download and extract information from a grib file. This requires you to use the ECMWF’s ecCodes which doesn’t work on Windows. To get around this the OpenDAP version of the forecast is used and a custom decoder reads the downloaded files.

206 |

Previous Python projects that attempted this do not fulfil all the requirements, mainly being an importable library. Acknowledgment must be made to albertotb’s project get-gfs for providing the first foothold along the way.

207 |
208 |

Requirements

209 |

The required libraries (installed by PyPi) are:

210 |

`scipy, requests, fuzzywuzzy, numpy, python_dateutil, regex`

211 |

I have tried to ensure that these are well maintained and work across platforms (as this was the motive for writing this library).

212 |
213 |
214 |
215 |

Contributing

216 |

Please see contributing for more information.

217 |
218 |

Todo

219 | 224 |
225 |

Contents:

226 | 236 |
237 |
238 |
239 |
240 |

Indices and tables

241 | 246 |
247 | 248 | 249 |
250 | 251 |
252 |
253 | 256 | 257 |
258 | 259 |
260 |

261 | © Copyright 2021, Jago Strong-Wright. 262 | 263 |

264 |
265 | 266 | 267 | 268 | Built with Sphinx using a 269 | 270 | theme 271 | 272 | provided by Read the Docs. 273 | 274 |
275 |
276 |
277 | 278 |
279 | 280 |
281 | 282 | 283 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | -------------------------------------------------------------------------------- /docs/build/html/modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | getgfs — getgfs 1.0.0 documentation 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | 97 | 98 |
99 | 100 | 101 | 107 | 108 | 109 |
110 | 111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
132 | 133 |
    134 | 135 |
  • »
  • 136 | 137 |
  • getgfs
  • 138 | 139 | 140 |
  • 141 | 142 | 143 | View page source 144 | 145 | 146 |
  • 147 | 148 |
149 | 150 | 151 |
152 |
153 |
154 |
155 | 156 |
157 |

getgfs

158 |
159 | 169 |
170 |
171 | 172 | 173 |
174 | 175 |
176 |
177 | 178 |
179 | 180 |
181 |

182 | © Copyright 2021, Jago Strong-Wright. 183 | 184 |

185 |
186 | 187 | 188 | 189 | Built with Sphinx using a 190 | 191 | theme 192 | 193 | provided by Read the Docs. 194 | 195 |
196 |
197 |
198 | 199 |
200 | 201 |
202 | 203 | 204 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /docs/build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/docs/build/html/objects.inv -------------------------------------------------------------------------------- /docs/build/html/py-modindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Python Module Index — getgfs 1.0.0 documentation 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 100 | 101 |
102 | 103 | 104 | 110 | 111 | 112 |
113 | 114 |
115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
135 | 136 |
    137 | 138 |
  • »
  • 139 | 140 |
  • Python Module Index
  • 141 | 142 | 143 |
  • 144 | 145 |
  • 146 | 147 |
148 | 149 | 150 |
151 |
152 |
153 |
154 | 155 | 156 |

Python Module Index

157 | 158 |
159 | g 160 |
161 | 162 | 163 | 164 | 166 | 167 | 169 | 172 | 173 | 174 | 177 | 178 | 179 | 182 | 183 | 184 | 187 |
 
165 | g
170 | getgfs 171 |
    175 | getgfs.decode 176 |
    180 | getgfs.getgfs 181 |
    185 | getgfs.test 186 |
188 | 189 | 190 |
191 | 192 |
193 |
194 | 195 |
196 | 197 |
198 |

199 | © Copyright 2021, Jago Strong-Wright. 200 | 201 |

202 |
203 | 204 | 205 | 206 | Built with Sphinx using a 207 | 208 | theme 209 | 210 | provided by Read the Docs. 211 | 212 |
213 |
214 |
215 | 216 |
217 | 218 |
219 | 220 | 221 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /docs/build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Search — getgfs 1.0.0 documentation 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 100 | 101 |
102 | 103 | 104 | 110 | 111 | 112 |
113 | 114 |
115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
135 | 136 |
    137 | 138 |
  • »
  • 139 | 140 |
  • Search
  • 141 | 142 | 143 |
  • 144 | 145 |
  • 146 | 147 |
148 | 149 | 150 |
151 |
152 |
153 |
154 | 155 | 162 | 163 | 164 |
165 | 166 |
167 | 168 |
169 | 170 |
171 |
172 | 173 |
174 | 175 |
176 |

177 | © Copyright 2021, Jago Strong-Wright. 178 | 179 |

180 |
181 | 182 | 183 | 184 | Built with Sphinx using a 185 | 186 | theme 187 | 188 | provided by Read the Docs. 189 | 190 |
191 |
192 |
193 | 194 |
195 | 196 |
197 | 198 | 199 | 204 | 205 | 206 | 207 | 208 | 209 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /docs/build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({docnames:["getgfs","index","modules"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,sphinx:56},filenames:["getgfs.rst","index.rst","modules.rst"],objects:{"":{getgfs:[0,0,0,"-"]},"getgfs.decode":{Coordinate:[0,1,1,""],File:[0,1,1,""],Variable:[0,1,1,""],replace_val:[0,2,1,""]},"getgfs.getgfs":{Forecast:[0,1,1,""],extract_line:[0,2,1,""],get_attributes:[0,2,1,""],hour_round:[0,2,1,""]},"getgfs.getgfs.Forecast":{datetime_to_forecast:[0,3,1,""],get:[0,3,1,""],get_windprofile:[0,3,1,""],search:[0,3,1,""],value_input_to_index:[0,3,1,""],value_to_index:[0,3,1,""]},"getgfs.test":{Decode:[0,1,1,""],TestBasics:[0,1,1,""]},"getgfs.test.Decode":{test_data:[0,3,1,""],test_variables:[0,3,1,""]},"getgfs.test.TestBasics":{test_attribute:[0,3,1,""],test_folders:[0,3,1,""]},getgfs:{decode:[0,0,0,"-"],getgfs:[0,0,0,"-"],test:[0,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","function","Python function"],"3":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:class","2":"py:function","3":"py:method"},terms:{"004":1,"0p25":[0,1],"0p50":[0,1],"100":1,"1000":1,"11pm":0,"125":1,"133":1,"150":[],"175":[],"1829":1,"1hr":[0,1],"1p00":[0,1],"200":[],"202003":1,"20200328":1,"20210205":0,"20210227":1,"250":[],"2743":1,"27th":1,"3658":1,"3hr":0,"808477":1,"900":1,"925":1,"950":1,"975":1,"995":1,"999e":1,"case":0,"class":0,"default":0,"export":1,"float":0,"function":1,"import":1,"int":0,"long":0,"return":0,"short":0,For:[],GFS:[0,1],The:[0,1],Will:0,_fillvalu:1,about:0,abov:1,accumul:[],acknowledg:1,across:1,add:1,adding:1,after:[0,1],albertotb:1,all:[0,1],along:1,also:[0,1],altitud:[0,1],ani:[0,1],anoth:0,appropri:0,arg:0,around:1,arr:0,arrai:[0,1],atmospher:1,attempt:1,attribut:0,avail:[0,1],back:1,base:0,being:[0,1],better:0,block:[],boundari:1,can:[0,1],categor:[],chang:0,check:1,choic:0,chose:1,chosen:0,code:[],com:[],common:0,compoent:0,compon:[0,1],connect:1,content:[1,2],convert:0,coord:0,coordin:0,could:1,couldn:0,creat:1,csnowavesfc:[],csnowsfc:[],current:1,custom:1,das:[0,1],data:[0,1],date:0,date_tim:0,datetim:0,datetime_to_forcast:[],datetime_to_forecast:0,dds:0,decod:[1,2],depend:[0,1],depth:[],describ:0,descript:0,desir:0,dict:0,dimension:0,doc:[],document:0,dodsc:1,doesn:1,down:0,download:[0,1],eccod:1,ecmwf:1,ensur:1,equival:[],exampl:1,except:0,explor:1,extract:[0,1],extract_lin:0,extrem:0,fail:0,failur:0,fals:1,febuari:1,file:[0,1],fill:1,find:[0,1],fine:0,first:1,foothold:1,forcast:[],forecast:[0,1],format:0,forward:1,from:[0,1],fulfil:1,full:[],furutr:1,fuzzywuzzi:[0,1],geopotenti:1,get:[0,1],get_attribut:0,get_windprofil:0,gfs:1,gfs_4_20200328_1800_384:1,github:[],given:0,going:1,gov:1,gpm:1,grb2:1,grib:1,ground:1,gust:1,gustsfc:1,hard:0,has:0,have:[0,1],height:1,hgtmwl:1,high:0,higher:0,histor:1,hold:0,hour:0,hour_round:0,http:1,hybrid:[],icahtmwl:1,icao:1,impliment:0,incent:1,includ:[0,1],incorrect:0,index:[0,1],inform:[0,1],inpt:0,insert:0,instal:0,interpol:[0,1],interpolat:0,invalid:0,iter:1,lat:0,latest:0,latitud:[0,1],layer:1,level:[0,1],level_depend:1,librari:[0,1],line:0,lineno:[],list:[0,1],locat:0,lon:0,long_nam:1,longitud:[0,1],look:[0,1],made:1,mai:0,mainli:1,maintain:1,manipul:0,match:0,max:[0,1],max_lat:1,max_val:0,mayb:1,mean:1,method:1,methodnam:0,might:1,min:0,min_lat:1,min_val:0,miss:1,missing_valu:1,mix:[],model:1,modul:[1,2],more:1,most:0,motiv:1,multipl:1,must:1,n70:1,name:[0,1],ncei:1,ncep:[],nearest:0,need:0,netcdf4:1,noaa:[0,1],nomad:[],nonsenc:0,now:1,number:0,numpi:[0,1],object:[0,1],obscur:[0,1],old:1,onli:1,opendap:1,option:[0,1],other:0,otherwis:0,out:0,packag:[1,2],page:[0,1],paramet:[0,1],parser:0,pip:1,place:1,planetari:1,platform:1,pleas:1,posit:[0,1],possibl:0,presmwl:1,pressur:[0,1],previou:1,probabl:1,process:0,profil:[0,1],program:0,project:1,provid:1,pure:[0,1],purg:1,put:0,pypi:1,python:[0,1],python_dateutil:1,queri:0,quit:0,rais:0,rang:[0,1],rather:0,ratio:0,raw:0,read:1,refer:[0,1],regex:1,replace_v:0,request:[0,1],requir:0,res:[0,1],resolut:[0,1],rest:0,resulut:0,round:0,run:0,runtest:0,runtimeerror:0,scipi:1,sea:1,search:[0,1],search_nam:[],see:[0,1],seem:0,senset:0,sensit:0,shorter:1,sigma:[0,1],simpl:1,sinc:0,singl:0,site:0,size:0,snmrhy1:[],snmrpr:[],snodsfc:[],snow:[],sort:0,specifi:[0,1],speed:1,standard:1,start:1,step:0,str:0,straight:1,string:[0,1],submodul:[1,2],sure:0,surfac:[0,1],system:[],temperatur:1,term:0,test:[1,2],test_attribut:0,test_data:0,test_fold:0,test_vari:0,testbas:0,testcas:0,text:0,than:0,them:0,thi:[0,1],thredd:1,through:1,time:[0,1],timestep:[0,1],titl:0,tmpmwl:1,todo:0,too:0,tri:1,tropopaus:1,tupl:0,turn:0,type:[0,1],typeerror:0,ugrd100m:1,ugrd10m:1,ugrd20m:1,ugrd2pv:1,ugrd30_0mb:1,ugrd30m:1,ugrd40m:1,ugrd50m:1,ugrd80m:1,ugrd_1829m:1,ugrd_2743m:1,ugrd_3658m:1,ugrdmwl:1,ugrdneg2pv:1,ugrdpbl:1,ugrdpr:1,ugrdsig995:1,ugrdtrop:1,unit:1,unittest:0,unknown:0,unreli:1,use:[0,1],used:[0,1],using:1,val:0,valid:0,valu:[0,1],value_input_to_index:0,value_to_index:0,valueerror:0,variabl:[0,1],version:1,verticl:0,vgrd100m:1,vgrd10m:1,vgrd20m:1,vgrd2pv:1,vgrd30_0mb:1,vgrd30m:1,vgrd40m:1,vgrd50m:1,vgrd80m:1,vgrd_1829m:1,vgrd_2743m:1,vgrd_3658m:1,vgrdmwl:1,vgrdneg2pv:1,vgrdpbl:1,vgrdpr:1,vgrdsig995:1,vgrdtrop:1,w94:1,wai:[0,1],want:1,water:[],weasdsfc:[],weather:[0,1],week:1,well:[0,1],what:[0,1],where:1,which:[0,1],wind:[0,1],window:1,within:0,word:0,work:[0,1],write:1,wrote:0,www:1,you:[0,1]},titles:["getgfs package","Welcome to getgfs","getgfs"],titleterms:{about:1,content:0,contribut:1,decod:0,document:[],getgf:[0,1,2],gfspy:[],indic:1,instal:1,modul:0,packag:0,requir:1,submodul:0,tabl:1,test:0,todo:1,usag:1,welcom:1}}) -------------------------------------------------------------------------------- /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% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /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 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 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 | import sphinx_rtd_theme 16 | 17 | sys.path.insert(0, os.path.split(os.path.abspath(".."))[0]) 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = "getgfs" 23 | copyright = "2021, Jago Strong-Wright" 24 | author = "Jago Strong-Wright" 25 | 26 | # The full version, including alpha/beta/rc tags 27 | release = "1.0.0" 28 | 29 | 30 | # -- General configuration --------------------------------------------------- 31 | 32 | # Add any Sphinx extension module names here, as strings. They can be 33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 34 | # ones. 35 | extensions = [] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ["_templates"] 39 | 40 | # List of patterns, relative to source directory, that match files and 41 | # directories to ignore when looking for source files. 42 | # This pattern also affects html_static_path and html_extra_path. 43 | exclude_patterns = [] 44 | 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 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"] 59 | -------------------------------------------------------------------------------- /docs/source/getgfs.rst: -------------------------------------------------------------------------------- 1 | getgfs package 2 | ============== 3 | 4 | Submodules 5 | ---------- 6 | 7 | getgfs.decode module 8 | -------------------- 9 | 10 | .. automodule:: getgfs.decode 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | getgfs.getgfs module 16 | -------------------- 17 | 18 | .. automodule:: getgfs.getgfs 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | getgfs.test module 24 | ------------------ 25 | 26 | .. automodule:: getgfs.test 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | Module contents 32 | --------------- 33 | 34 | .. automodule:: getgfs 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. getgfs documentation master file, created by 2 | sphinx-quickstart on Sun Feb 28 19:01:16 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to getgfs 7 | ================ 8 | 9 | getgfs extracts weather forecast variables from the NOAA GFS forecast in a pure python, no obscure dependencies way. Currently you can: 10 | 11 | - "Connect" to a forecast 12 | - Search the variables 13 | - Download variables for any time range, longitude, latitude and altitude 14 | - Download "wind profiles" where you get an interpolation object for the u and v wind components by altitude 15 | 16 | Installing 17 | ========== 18 | Installation is simple with PyPi: 19 | 20 | ```pip install getgfs``` 21 | 22 | Usage 23 | ===== 24 | The library is straight forward to use. To get started create a Forecast object by: 25 | 26 | .. code-block:: python 27 | :linenos: 28 | 29 | >>>import getgfs 30 | >>>f=getgfs.Forecast("0p25") 31 | 32 | You can chose the resolution to be `0p25`, `0p50` or `1p00` and for the `0p25` forecast you can optional specify a shorter timestep by adding `1hr` after. 33 | 34 | First to find what variable you are looking for use the search function, for example if I want the wind speed I could search for "wind": 35 | 36 | .. code-block:: python 37 | :linenos: 38 | 39 | >>>f.search("wind") 40 | [('gustsfc', '** surface wind speed (gust) [m/s] ', 100), ('ugrdprs', '** (1000 975 950 925 900.. 7 5 3 2 1) u-component of wind [m/s] ', 125), ('ugrd_1829m', '** 1829 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd_2743m', '** 2743 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd_3658m', '** 3658 m above mean sea level u-component of wind [m/s] ', 125), ('ugrd10m', '** 10 m above ground u-component of wind [m/s] ', 125), ('ugrd20m', '** 20 m above ground u-component of wind [m/s] ', 125), ('ugrd30m', '** 30 m above ground u-component of wind [m/s] ', 125), ('ugrd40m', '** 40 m above ground u-component of wind [m/s] ', 125), ('ugrd50m', '** 50 m above ground u-component of wind [m/s] ', 125), ('ugrd80m', '** 80 m above ground u-component of wind [m/s] ', 125), ('ugrd100m', '** 100 m above ground u-component of wind [m/s] ', 125), ('ugrdsig995', '** 0.995 sigma level u-component of wind [m/s] ', 125), ('ugrd30_0mb', '** 30-0 mb above ground u-component of wind [m/s] ', 125), ('ugrd2pv', '** pv=2e-06 (km^2/kg/s) surface u-component of wind [m/s] ', 125), ('ugrdneg2pv', '** pv=-2e-06 (km^2/kg/s) surface u-component of wind [m/s] ', 125), ('ugrdpbl', '** planetary boundary layer u-component of wind [m/s] ', 125), ('ugrdtrop', '** tropopause u-component of wind [m/s] ', 125), ('vgrdprs', '** (1000 975 950 925 900.. 7 5 3 2 1) v-component of wind [m/s] ', 125), ('vgrd_1829m', '** 1829 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd_2743m', '** 2743 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd_3658m', '** 3658 m above mean sea level v-component of wind [m/s] ', 125), ('vgrd10m', '** 10 m above ground v-component of wind [m/s] ', 125), ('vgrd20m', '** 20 m above ground v-component of wind [m/s] ', 125), ('vgrd30m', '** 30 m above ground v-component of wind [m/s] ', 125), ('vgrd40m', '** 40 m above ground v-component of wind [m/s] ', 125), ('vgrd50m', '** 50 m above ground v-component of wind [m/s] ', 125), ('vgrd80m', '** 80 m above ground v-component of wind [m/s] ', 125), ('vgrd100m', '** 100 m above ground v-component of wind [m/s] ', 125), ('vgrdsig995', '** 0.995 sigma level v-component of wind [m/s] ', 125), ('vgrd30_0mb', '** 30-0 mb above ground v-component of wind [m/s] ', 125), ('vgrd2pv', '** pv=2e-06 (km^2/kg/s) surface v-component of wind [m/s] ', 125), ('vgrdneg2pv', '** pv=-2e-06 (km^2/kg/s) surface v-component of wind [m/s] ', 125), ('vgrdpbl', '** planetary boundary layer v-component of wind [m/s] ', 125), ('vgrdtrop', '** tropopause v-component of wind [m/s] ', 125), ('hgtmwl', '** max wind geopotential height [gpm] ', 133), ('icahtmwl', '** max wind icao standard atmosphere reference height [m] ', 133), ('presmwl', '** max wind pressure [pa] ', 133), ('tmpmwl', '** max wind temperature [k] ', 133), ('ugrdmwl', '** max wind u-component of wind [m/s] ', 133), ('vgrdmwl', '** max wind v-component of wind [m/s] ', 133)] 41 | 42 | So now I can see I might want "gustsfc". Now if I want the wind speed at N70.1 W94.7 at 5:30 on the 27th of Febuary (only forecasts going back around a week are available and furutre times available depend on the forecast - look for f.times) I could do: 43 | 44 | .. code-block:: python 45 | :linenos: 46 | 47 | >>>res=f.get(["gustsfc"],"20210227 5:30", 70.1,-94.7) 48 | >>>res.variables["gustsfc"].data 49 | array([[[18.808477]]]) 50 | 51 | You can get more information (e.g. what is the units of this) by exploring the variables information 52 | 53 | .. code-block:: python 54 | :linenos: 55 | 56 | >>>f.variables["gustsfc"] 57 | {'_FillValue': 9.999e+20, 'missing_value': 9.999e+20, 'long_name': '** surface wind speed (gust) [m/s] ', 'level_dependent': False} 58 | 59 | You can also get multiple variables by including more names in the list or a range of positions by using "'[min_lat:max_lat]'" type strings in place of the position parameters. 60 | 61 | About 62 | ===== 63 | 64 | The incentive to write this library was that the current method to get any variable was to download and extract information from a grib file. This requires you to use the ECMWF's `ecCodes` which `doesn't work on Windows `_. To get around this the `OpenDAP `_ version of the forecast is used and a custom decoder reads the downloaded files. 65 | 66 | Previous Python projects that attempted this do not fulfil all the requirements, mainly being an importable library. Acknowledgment must be made to `albertotb `_'s project `get-gfs `_ for providing the first foothold along the way. 67 | 68 | 69 | Requirements 70 | ------------ 71 | The required libraries (installed by PyPi) are: 72 | 73 | ```scipy, requests, fuzzywuzzy, numpy, python_dateutil, regex``` 74 | 75 | I have tried to ensure that these are well maintained and work across platforms (as this was the motive for writing this library). 76 | 77 | Contributing 78 | ============ 79 | Please see `contributing `_ for more information. 80 | 81 | Todo 82 | ---- 83 | - Add historical forecasts from ``_ 84 | - Add export to .nc file with netcdf4 (maybe an optional dependency) 85 | - Add purge missing/unreliable (missing/unreliable fill values are provided but have to iterate through data to check probably) 86 | 87 | .. toctree:: 88 | :maxdepth: 2 89 | :caption: Contents: 90 | 91 | getgfs.rst 92 | 93 | 94 | 95 | Indices and tables 96 | ================== 97 | 98 | * :ref:`genindex` 99 | * :ref:`modindex` 100 | * :ref:`search` 101 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | getgfs 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | getgfs 8 | -------------------------------------------------------------------------------- /getgfs/__init__.py: -------------------------------------------------------------------------------- 1 | from .getgfs import * 2 | -------------------------------------------------------------------------------- /getgfs/__pycache__/gfspy.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/getgfs/__pycache__/gfspy.cpython-38.pyc -------------------------------------------------------------------------------- /getgfs/config.json: -------------------------------------------------------------------------------- 1 | {"saved_atts": ["Na", "0p25_1hr"]} -------------------------------------------------------------------------------- /getgfs/decode.py: -------------------------------------------------------------------------------- 1 | """Decodes the downloaded files to extract the variables and their coordinates""" 2 | import re 3 | import numpy as np 4 | 5 | 6 | class Variable: 7 | """Holds the information and data for an extracted variable""" 8 | 9 | def __init__(self, name, coords, data): 10 | """Create variable 11 | 12 | Args: 13 | name (string): Short name of variable 14 | coords (dict): Dictionary of coordinate objects by name 15 | data (numpy array): Variable data 16 | """ 17 | self.name = name 18 | self.coords = coords 19 | self.data = data 20 | 21 | def __str__(self): 22 | print(type(self)) 23 | return self.name 24 | 25 | 26 | class Coordinate: 27 | """Holds the information and values describing a coordinate""" 28 | 29 | def __init__(self, name, values): 30 | """Create coordinate 31 | 32 | Args: 33 | name (string): name of coordinate 34 | values (numpy array): Possible coordinate values 35 | """ 36 | self.name = name 37 | self.values = values 38 | 39 | def __str__(self): 40 | print(type(self)) 41 | return self.name 42 | 43 | 44 | class File: 45 | """Holds the variables and information from a text file returned by the forecast site""" 46 | 47 | def __init__(self, text): 48 | """Decode an OpenDAP https://nomads.ncep.noaa.gov/ text file 49 | 50 | Args: 51 | text (string): OpenDAP text file as a string 52 | """ 53 | text = text.splitlines() 54 | # Get variable name and dimensionality 55 | ind_head = 0 56 | variables = [] 57 | while ind_head < len(text): 58 | try: 59 | variable_name = re.findall("(.*?), ", text[ind_head])[0] 60 | except IndexError: 61 | raise ValueError("Likely that file entered was not the correct format") 62 | dims = re.findall("\[(.*?)\]", text[ind_head]) 63 | dims.reverse() 64 | lines_data = 0 65 | for dim in dims[1:]: 66 | lines_data = int(dim) * (lines_data + 1) 67 | dims.reverse() 68 | 69 | lines_meta = len(dims) * 2 # Starts +1 from end of lines data 70 | name_line = True 71 | coords = [] 72 | for line in text[ 73 | ind_head + 2 + lines_data : ind_head + 3 + lines_data + lines_meta 74 | ]: 75 | if name_line: 76 | name = re.findall("(.*?), ", line)[0] 77 | name_line = False 78 | else: 79 | coords.append( 80 | Coordinate(name, [float(v[:-1]) for v in line.split()]) 81 | ) 82 | name_line = True 83 | 84 | data = np.zeros(tuple([int(d) for d in dims])) 85 | data[:] = np.nan 86 | for line in text[ind_head + 1 : ind_head + 1 + lines_data - 1]: 87 | if len(line) > 0 and line[0] == "[": 88 | position = [int(v) for v in re.findall("\[(.*?)\]", line)] 89 | values = line.split()[1:] 90 | if len(values) > 1: 91 | for ind, value in enumerate(values): 92 | if value[-1] == ",": 93 | value = value[:-1] 94 | data = replace_val(data, float(value), position + [ind]) 95 | else: 96 | data = replace_val(data, float(values[0]), position) 97 | 98 | coords = {c.name: c for c in coords} 99 | variables.append(Variable(variable_name, coords, data)) 100 | 101 | ind_head += lines_data + lines_meta + 2 102 | 103 | self.variables = {v.name: v for v in variables} 104 | 105 | def __str__(self): 106 | print(type(self)) 107 | return "File containing %s" % self.variables.keys() 108 | 109 | 110 | def replace_val(arr, val, position): 111 | """Inserts a value into a 1 to 4 dimensional numpy array 112 | 113 | Note 114 | ---- 115 | I am sure there are better ways todo this but I couldn't find any after quite a search 116 | 117 | Args: 118 | arr (numpy array): Array to insert into 119 | val (float/int): Value to insert 120 | position (tuple): Coordinate position in array 121 | 122 | Raises: 123 | TypeError: Position invalid 124 | ValueError: Dimensionality of array too high 125 | 126 | Returns: 127 | [type]: [description] 128 | """ 129 | if not isinstance(position, list): 130 | raise TypeError("Wrong type entered for replacement position") 131 | # I wish I could find a proper way todo this, np.put only works for 1D arrays 132 | if len(position) == 1: 133 | arr[position[0]] = val 134 | elif len(position) == 2: 135 | arr[position[0]][position[1]] = val 136 | elif len(position) == 3: 137 | arr[position[0]][position[1]][position[2]] = val 138 | elif len(position) == 4: 139 | arr[position[0]][position[1]][position[2]][position[3]] = val 140 | else: 141 | raise ValueError( 142 | "Number of dimensions for value replacement not supported, please edit and make pull request it will be very easy" 143 | ) 144 | 145 | return arr 146 | -------------------------------------------------------------------------------- /getgfs/test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from .getgfs import * 3 | from .decode import * 4 | 5 | # Seems like these aren't actually working 6 | 7 | __copyright__ = """ 8 | getgfs - A library that extracts information from the NOAA GFS forecast without using .grb2 files 9 | Copyright (C) 2021 Jago Strong-Wright 10 | 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License 22 | along with this program. If not, see .""" 23 | 24 | example_file = """hgtprs, [5][4][2][4] 25 | [0][0][0], 143.26001, 143.26001, 143.26001, 143.26001 26 | [0][0][1], 142.28401, 142.268, 142.25201, 142.25201 27 | 28 | [0][1][0], 329.76636, 329.76636, 329.76636, 329.76636 29 | [0][1][1], 328.71036, 328.71036, 328.69437, 328.67834 30 | 31 | [0][2][0], 520.16754, 520.16754, 520.16754, 520.16754 32 | [0][2][1], 519.04755, 519.0315, 519.0155, 518.9995 33 | 34 | [0][3][0], 714.6294, 714.6294, 714.6294, 714.6294 35 | [0][3][1], 713.4134, 713.3974, 713.3974, 713.3814 36 | 37 | 38 | [1][0][0], 141.57744, 141.57744, 141.57744, 141.57744 39 | [1][0][1], 142.63344, 142.60144, 142.56944, 142.53745 40 | 41 | [1][1][0], 328.0829, 328.0829, 328.0829, 328.0829 42 | [1][1][1], 328.93088, 328.9149, 328.8829, 328.8509 43 | 44 | [1][2][0], 518.4631, 518.4631, 518.4631, 518.4631 45 | [1][2][1], 519.1191, 519.08704, 519.07104, 519.03906 46 | 47 | [1][3][0], 712.9098, 712.9098, 712.9098, 712.9098 48 | [1][3][1], 713.3578, 713.3418, 713.3258, 713.29376 49 | 50 | 51 | [2][0][0], 132.99606, 132.99606, 132.99606, 132.99606 52 | [2][0][1], 133.52406, 133.50807, 133.49207, 133.46007 53 | 54 | [2][1][0], 319.4645, 319.4645, 319.4645, 319.4645 55 | [2][1][1], 319.8485, 319.83252, 319.81653, 319.78452 56 | 57 | [2][2][0], 509.819, 509.819, 509.819, 509.819 58 | [2][2][1], 510.059, 510.043, 510.027, 510.011 59 | 60 | [2][3][0], 704.22687, 704.22687, 704.22687, 704.22687 61 | [2][3][1], 704.3229, 704.3068, 704.29083, 704.29083 62 | 63 | 64 | [3][0][0], 129.57916, 129.57916, 129.57916, 129.57916 65 | [3][0][1], 129.99516, 129.97916, 129.94716, 129.93117 66 | 67 | [3][1][0], 315.9453, 315.9453, 315.9453, 315.9453 68 | [3][1][1], 316.21732, 316.18533, 316.16934, 316.15332 69 | 70 | [3][2][0], 506.1779, 506.1779, 506.1779, 506.1779 71 | [3][2][1], 506.3059, 506.2899, 506.2739, 506.2579 72 | 73 | [3][3][0], 700.4825, 700.4825, 700.4825, 700.4825 74 | [3][3][1], 700.4665, 700.4505, 700.41846, 700.40247 75 | 76 | 77 | [4][0][0], 124.38838, 124.38838, 124.38838, 124.38838 78 | [4][0][1], 125.04438, 125.02838, 125.01238, 125.01238 79 | 80 | [4][1][0], 310.62332, 310.62332, 310.62332, 310.62332 81 | [4][1][1], 311.1353, 311.11932, 311.11932, 311.08734 82 | 83 | [4][2][0], 500.7546, 500.7546, 500.7546, 500.7546 84 | [4][2][1], 501.10663, 501.0906, 501.0906, 501.07462 85 | 86 | [4][3][0], 694.9354, 694.9354, 694.9354, 694.9354 87 | [4][3][1], 695.1274, 695.1274, 695.1114, 695.1114 88 | 89 | 90 | 91 | time, [5] 92 | 737842.0, 737842.125, 737842.25, 737842.375, 737842.5 93 | lev, [4] 94 | 1000.0, 975.0, 950.0, 925.0 95 | lat, [2] 96 | -90.0, -89.75 97 | lon, [4] 98 | 0.0, 0.25, 0.5, 0.75 99 | hgtmwl, [1][1][1] 100 | [0][0], 9504.847 101 | 102 | 103 | time, [1] 104 | 737842.0 105 | lat, [1] 106 | -90.0 107 | lon, [1] 108 | 0.0""" 109 | 110 | example = File(example_file) 111 | 112 | 113 | class TestBasics(unittest.TestCase): 114 | def test_attribute(self): 115 | self.assertEqual( 116 | Forecast("0p25", "1hr").times, {"grads_size": "121", "grads_step": "1hr"} 117 | ) 118 | 119 | def test_folders(self): 120 | if not os.path.isdir("%s/atts" % route) and not os.path.isfile(config_file): 121 | result = "Required files and folders are not being created" 122 | else: 123 | result = "Ok" 124 | self.assertEqual(result, "Ok") 125 | 126 | 127 | class Decode(unittest.TestCase): 128 | def test_variables(self): 129 | self.assertEqual( 130 | example.variables["hgtprs"].coords["time"].values, 131 | [737842.0, 737842.125, 737842.25, 737842.375, 737842.0], 132 | ) 133 | 134 | def test_data(self): 135 | self.assertEqual(example.variables["hgtmwl"].data, [[[9504.847]]]) 136 | 137 | 138 | if __name__ == "__main__": 139 | unittest.main() 140 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagoosw/getgfs/4f2b83f146c364cbc5c1b918a9f7704781e9b490/logo.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | scipy 2 | requests 3 | fuzzywuzzy 4 | numpy 5 | python_dateutil 6 | regex 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # Inside of setup.cfg 2 | [metadata] 3 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | # Application name: 5 | name="getgfs", 6 | # Version number (initial): 7 | version="1.2.0", 8 | # Application author details: 9 | author="Jago Strong-Wright", 10 | author_email="jagoosw@protonmail.com", 11 | # Packages 12 | packages=["getgfs"], 13 | # Details 14 | url="https://getgfs.readthedocs.io/", 15 | # 16 | license="LICENSE.txt", 17 | description="getgfs extracts weather forecast variables from the NOAA GFS forecast", 18 | # Dependent packages (distributions) 19 | install_requires=[ 20 | "scipy", 21 | "requests", 22 | "fuzzywuzzy", 23 | "numpy", 24 | "python_dateutil", 25 | "regex", 26 | ], 27 | classifiers=[ 28 | "Programming Language :: Python :: 3", 29 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 30 | "Operating System :: OS Independent", 31 | "Topic :: Text Processing :: Markup :: Markdown", 32 | ], 33 | ) 34 | --------------------------------------------------------------------------------