python-awips markdown examples added to ghpages AWIPS user manual
BIN
docs/images/cartopy_2_1.png
Normal file
After Width: | Height: | Size: 197 KiB |
BIN
docs/images/cartopy_4_0.png
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
docs/images/map_11_1.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
docs/images/map_13_1.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
docs/images/map_15_1.png
Normal file
After Width: | Height: | Size: 137 KiB |
BIN
docs/images/map_18_1.png
Normal file
After Width: | Height: | Size: 375 KiB |
BIN
docs/images/map_4_1.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/images/map_6_0.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
docs/images/map_8_1.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
docs/images/output_0_1.png
Normal file
After Width: | Height: | Size: 110 KiB |
BIN
docs/images/output_11_1.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
docs/images/output_13_1.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
docs/images/output_14_1.png
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
docs/images/output_15_1.png
Normal file
After Width: | Height: | Size: 137 KiB |
BIN
docs/images/output_18_1.png
Normal file
After Width: | Height: | Size: 375 KiB |
BIN
docs/images/output_1_0.png
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
docs/images/output_3_0.png
Normal file
After Width: | Height: | Size: 141 KiB |
BIN
docs/images/output_3_1.png
Normal file
After Width: | Height: | Size: 169 KiB |
BIN
docs/images/output_4_1.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/images/output_5_0.png
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
docs/images/output_6_0.png
Normal file
After Width: | Height: | Size: 209 KiB |
BIN
docs/images/output_7_0.png
Normal file
After Width: | Height: | Size: 1,000 KiB |
BIN
docs/images/output_8_1.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
docs/images/output_9_1.png
Normal file
After Width: | Height: | Size: 361 KiB |
BIN
docs/images/warngen_nexrad_display.png
Normal file
After Width: | Height: | Size: 438 KiB |
|
@ -42,7 +42,7 @@ Unidata supports two visualization frameworks for rendering data: [CAVE](install
|
|||
|----------------|-----------------------:|
|
||||
| Linux x86_64 | [installEDEX.sh <i class="fa fa-download"></i>](http://www.unidata.ucar.edu/software/awips2/installEDEX.sh) |
|
||||
|
||||
[Read full EDEX install instructions...](install/install-edex.html)
|
||||
[Read full EDEX install instructions...](install/install-edex)
|
||||
|
||||
---
|
||||
|
||||
|
|
75
docs/python/awips-grids-and-cartopy.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
|
||||
The simplest example of requesting and plotting AWIPS gridded data with Matplotlib and Cartopy.
|
||||
|
||||
|
||||
```python
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
import cartopy.crs as ccrs
|
||||
import matplotlib.pyplot as plt
|
||||
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
|
||||
%matplotlib inline
|
||||
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
request.setDatatype("grid")
|
||||
request.setLocationNames("RAP13")
|
||||
request.setParameters("T")
|
||||
request.setLevels("2.0FHAG")
|
||||
cycles = DataAccessLayer.getAvailableTimes(request, True)
|
||||
times = DataAccessLayer.getAvailableTimes(request)
|
||||
fcstRun = DataAccessLayer.getForecastRun(cycles[-1], times)
|
||||
response = DataAccessLayer.getGridData(request, [fcstRun[0]])
|
||||
grid = response[0]
|
||||
data = grid.getRawData()
|
||||
lons, lats = grid.getLatLonCoords()
|
||||
bbox = [lons.min(), lons.max(), lats.min(), lats.max()]
|
||||
|
||||
def make_map(bbox, projection=ccrs.PlateCarree()):
|
||||
fig, ax = plt.subplots(figsize=(16, 9),
|
||||
subplot_kw=dict(projection=projection))
|
||||
ax.set_extent(bbox)
|
||||
ax.coastlines(resolution='50m')
|
||||
gl = ax.gridlines(draw_labels=True)
|
||||
gl.xlabels_top = gl.ylabels_right = False
|
||||
gl.xformatter = LONGITUDE_FORMATTER
|
||||
gl.yformatter = LATITUDE_FORMATTER
|
||||
return fig, ax
|
||||
```
|
||||
|
||||
### with pcolormesh
|
||||
|
||||
|
||||
```python
|
||||
cmap = plt.get_cmap('rainbow')
|
||||
fig, ax = make_map(bbox=bbox)
|
||||
cs = ax.pcolormesh(lons, lats, data, cmap=cmap)
|
||||
cbar = fig.colorbar(cs, shrink=0.7, orientation='horizontal')
|
||||
cbar.set_label(str(grid.getLocationName()) +" " \
|
||||
+ str(grid.getLevel()) + " " \
|
||||
+ str(grid.getParameter()) \
|
||||
+ " (" + str(grid.getUnit()) + ") " \
|
||||
+ "valid " + str(grid.getDataTime().getRefTime()))
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
### with contourf
|
||||
|
||||
|
||||
```python
|
||||
fig2, ax2 = make_map(bbox=bbox)
|
||||
cs2 = ax2.contourf(lons, lats, data, 80, cmap=cmap,
|
||||
vmin=data.min(), vmax=data.max())
|
||||
cbar2 = fig2.colorbar(cs2, shrink=0.7, orientation='horizontal')
|
||||
cbar2.set_label(str(grid.getLocationName()) +" " \
|
||||
+ str(grid.getLevel()) + " " \
|
||||
+ str(grid.getParameter()) \
|
||||
+ " (" + str(grid.getUnit()) + ") " \
|
||||
+ "valid " + str(grid.getDataTime().getRefTime()))
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
373
docs/python/map-resources-and-topography.md
Normal file
|
@ -0,0 +1,373 @@
|
|||
|
||||
The python-awips package provides access to the entire AWIPS Maps Database for use in Python GIS applications. Map objects are returned as <a href="http://toblerity.org/shapely/manual.html">Shapely</a> geometries (*Polygon*, *Point*, *MultiLineString*, etc.) and can be easily plotted by Matplotlib, Cartopy, MetPy, and other packages.
|
||||
|
||||
Each map database table has a geometry field called `the_geom`, which can be used to spatially select map resources for any column of type geometry,
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
* This notebook requires: **python-awips, numpy, matplotplib, cartopy, shapely**
|
||||
* Use datatype **maps** and **addIdentifier('table', <postgres maps schema>)** to define the map table:
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
request = DataAccessLayer.newDataRequest('maps')
|
||||
request.addIdentifier('table', 'mapdata.county')
|
||||
* Use **request.setLocationNames()** and **request.addIdentifier()** to spatially filter a map resource. In the example below, WFO ID **BOU** (Boulder, Colorado) is used to query counties within the BOU county watch area (CWA)
|
||||
|
||||
request.addIdentifier('geomField', 'the_geom')
|
||||
request.addIdentifier('inLocation', 'true')
|
||||
request.addIdentifier('locationField', 'cwa')
|
||||
request.setLocationNames('BOU')
|
||||
request.addIdentifier('cwa', 'BOU')
|
||||
|
||||
See the <a href="http://unidata.github.io/awips2/python/maps-database/#mapdatacwa">Maps Database Reference Page</a> for available database tables, column names, and types.
|
||||
|
||||
> Note the geometry definition of `the_geom` for each data type, which can be **Point**, **MultiPolygon**, or **MultiLineString**.
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
|
||||
```python
|
||||
from __future__ import print_function
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
import matplotlib.pyplot as plt
|
||||
import cartopy.crs as ccrs
|
||||
import numpy as np
|
||||
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
|
||||
from cartopy.feature import ShapelyFeature,NaturalEarthFeature
|
||||
from shapely.geometry import Polygon
|
||||
from shapely.ops import cascaded_union
|
||||
|
||||
# Standard map plot
|
||||
def make_map(bbox, projection=ccrs.PlateCarree()):
|
||||
fig, ax = plt.subplots(figsize=(12,12),
|
||||
subplot_kw=dict(projection=projection))
|
||||
ax.set_extent(bbox)
|
||||
ax.coastlines(resolution='50m')
|
||||
gl = ax.gridlines(draw_labels=True)
|
||||
gl.xlabels_top = gl.ylabels_right = False
|
||||
gl.xformatter = LONGITUDE_FORMATTER
|
||||
gl.yformatter = LATITUDE_FORMATTER
|
||||
return fig, ax
|
||||
|
||||
# Server, Data Request Type, and Database Table
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
request = DataAccessLayer.newDataRequest('maps')
|
||||
request.addIdentifier('table', 'mapdata.county')
|
||||
```
|
||||
|
||||
## Request County Boundaries for a WFO
|
||||
|
||||
* Use **request.setParameters()** to define fields to be returned by the request.
|
||||
|
||||
|
||||
```python
|
||||
# Define a WFO ID for location
|
||||
# tie this ID to the mapdata.county column "cwa" for filtering
|
||||
request.setLocationNames('BOU')
|
||||
request.addIdentifier('cwa', 'BOU')
|
||||
|
||||
# enable location filtering (inLocation)
|
||||
# locationField is tied to the above cwa definition (BOU)
|
||||
request.addIdentifier('geomField', 'the_geom')
|
||||
request.addIdentifier('inLocation', 'true')
|
||||
request.addIdentifier('locationField', 'cwa')
|
||||
|
||||
# This is essentially the same as "'"select count(*) from mapdata.cwa where cwa='BOU';" (=1)
|
||||
|
||||
# Get response and create dict of county geometries
|
||||
response = DataAccessLayer.getGeometryData(request, [])
|
||||
counties = np.array([])
|
||||
for ob in response:
|
||||
counties = np.append(counties,ob.getGeometry())
|
||||
print("Using " + str(len(counties)) + " county MultiPolygons")
|
||||
|
||||
|
||||
%matplotlib inline
|
||||
# All WFO counties merged to a single Polygon
|
||||
merged_counties = cascaded_union(counties)
|
||||
envelope = merged_counties.buffer(2)
|
||||
boundaries=[merged_counties]
|
||||
|
||||
# Get bounds of this merged Polygon to use as buffered map extent
|
||||
bounds = merged_counties.bounds
|
||||
bbox=[bounds[0]-1,bounds[2]+1,bounds[1]-1.5,bounds[3]+1.5]
|
||||
|
||||
|
||||
fig, ax = make_map(bbox=bbox)
|
||||
# Plot political/state boundaries handled by Cartopy
|
||||
political_boundaries = NaturalEarthFeature(category='cultural',
|
||||
name='admin_0_boundary_lines_land',
|
||||
scale='50m', facecolor='none')
|
||||
states = NaturalEarthFeature(category='cultural',
|
||||
name='admin_1_states_provinces_lines',
|
||||
scale='50m', facecolor='none')
|
||||
ax.add_feature(political_boundaries, linestyle='-', edgecolor='black')
|
||||
ax.add_feature(states, linestyle='-', edgecolor='black',linewidth=2)
|
||||
|
||||
# Plot CWA counties
|
||||
for i, geom in enumerate(counties):
|
||||
cbounds = Polygon(geom)
|
||||
intersection = cbounds.intersection
|
||||
geoms = (intersection(geom)
|
||||
for geom in counties
|
||||
if cbounds.intersects(geom))
|
||||
shape_feature = ShapelyFeature(geoms,ccrs.PlateCarree(),
|
||||
facecolor='none', linestyle="-",edgecolor='#86989B')
|
||||
ax.add_feature(shape_feature)
|
||||
```
|
||||
|
||||
Using 25 county MultiPolygons
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
## Create a merged CWA with cascaded_union
|
||||
|
||||
|
||||
```python
|
||||
# Plot CWA envelope
|
||||
for i, geom in enumerate(boundaries):
|
||||
gbounds = Polygon(geom)
|
||||
intersection = gbounds.intersection
|
||||
geoms = (intersection(geom)
|
||||
for geom in boundaries
|
||||
if gbounds.intersects(geom))
|
||||
shape_feature = ShapelyFeature(geoms,ccrs.PlateCarree(),
|
||||
facecolor='none', linestyle="-",linewidth=3.,edgecolor='#cc5000')
|
||||
ax.add_feature(shape_feature)
|
||||
|
||||
fig
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## WFO boundary spatial filter for interstates
|
||||
|
||||
Using the previously-defined **envelope=merged_counties.buffer(2)** in **newDataRequest()** to request geometries which fall inside the buffered boundary.
|
||||
|
||||
|
||||
```python
|
||||
request = DataAccessLayer.newDataRequest('maps', envelope=envelope)
|
||||
request.addIdentifier('table', 'mapdata.interstate')
|
||||
request.addIdentifier('geomField', 'the_geom')
|
||||
request.addIdentifier('locationField', 'hwy_type')
|
||||
request.addIdentifier('hwy_type', 'I') # I (interstate), U (US highway), or S (state highway)
|
||||
request.setParameters('name')
|
||||
interstates = DataAccessLayer.getGeometryData(request, [])
|
||||
print("Using " + str(len(interstates)) + " interstate MultiLineStrings")
|
||||
|
||||
# Plot interstates
|
||||
for ob in interstates:
|
||||
shape_feature = ShapelyFeature(ob.getGeometry(),ccrs.PlateCarree(),
|
||||
facecolor='none', linestyle="-",edgecolor='orange')
|
||||
ax.add_feature(shape_feature)
|
||||
fig
|
||||
```
|
||||
|
||||
Using 223 interstate MultiLineStrings
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
> Road type from `select distinct(hwy_type) from mapdata.interstate;`
|
||||
>
|
||||
> I - Interstates
|
||||
> U - US Highways
|
||||
> S - State Highways
|
||||
|
||||
|
||||
## Nearby cities
|
||||
|
||||
Request the city table and filter by population and progressive disclosure level:
|
||||
|
||||
**Warning**: the `prog_disc` field is not entirely understood and values appear to change significantly depending on WFO site.
|
||||
|
||||
|
||||
```python
|
||||
request = DataAccessLayer.newDataRequest('maps', envelope=envelope)
|
||||
request.addIdentifier('table', 'mapdata.city')
|
||||
request.addIdentifier('geomField', 'the_geom')
|
||||
request.setParameters('name','population','prog_disc')
|
||||
cities = DataAccessLayer.getGeometryData(request, [])
|
||||
print("Found " + str(len(cities)) + " city Points")
|
||||
```
|
||||
|
||||
Found 1201 city Points
|
||||
|
||||
|
||||
|
||||
```python
|
||||
citylist = []
|
||||
cityname = []
|
||||
# For BOU, progressive disclosure values above 50 and pop above 5000 looks good
|
||||
for ob in cities:
|
||||
if ((ob.getNumber("prog_disc")>50) and int(ob.getString("population")) > 5000):
|
||||
citylist.append(ob.getGeometry())
|
||||
cityname.append(ob.getString("name"))
|
||||
print("Using " + str(len(cityname)) + " city Points")
|
||||
|
||||
# Plot city markers
|
||||
ax.scatter([point.x for point in citylist],
|
||||
[point.y for point in citylist],
|
||||
transform=ccrs.Geodetic(),marker="+",facecolor='black')
|
||||
# Plot city names
|
||||
for i, txt in enumerate(cityname):
|
||||
ax.annotate(txt, (citylist[i].x,citylist[i].y),
|
||||
xytext=(3,3), textcoords="offset points")
|
||||
|
||||
fig
|
||||
```
|
||||
|
||||
Using 57 city Points
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## Lakes
|
||||
|
||||
|
||||
```python
|
||||
request = DataAccessLayer.newDataRequest('maps', envelope=envelope)
|
||||
request.addIdentifier('table', 'mapdata.lake')
|
||||
request.addIdentifier('geomField', 'the_geom')
|
||||
request.setParameters('name')
|
||||
|
||||
# Get lake geometries
|
||||
response = DataAccessLayer.getGeometryData(request, [])
|
||||
lakes = np.array([])
|
||||
for ob in response:
|
||||
lakes = np.append(lakes,ob.getGeometry())
|
||||
print("Using " + str(len(lakes)) + " lake MultiPolygons")
|
||||
|
||||
# Plot lakes
|
||||
for i, geom in enumerate(lakes):
|
||||
cbounds = Polygon(geom)
|
||||
intersection = cbounds.intersection
|
||||
geoms = (intersection(geom)
|
||||
for geom in lakes
|
||||
if cbounds.intersects(geom))
|
||||
shape_feature = ShapelyFeature(geoms,ccrs.PlateCarree(),
|
||||
facecolor='blue', linestyle="-",edgecolor='#20B2AA')
|
||||
ax.add_feature(shape_feature)
|
||||
fig
|
||||
|
||||
```
|
||||
|
||||
Using 208 lake MultiPolygons
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## Major Rivers
|
||||
|
||||
|
||||
```python
|
||||
request = DataAccessLayer.newDataRequest('maps', envelope=envelope)
|
||||
request.addIdentifier('table', 'mapdata.majorrivers')
|
||||
request.addIdentifier('geomField', 'the_geom')
|
||||
request.setParameters('pname')
|
||||
rivers = DataAccessLayer.getGeometryData(request, [])
|
||||
print("Using " + str(len(rivers)) + " river MultiLineStrings")
|
||||
|
||||
# Plot rivers
|
||||
for ob in rivers:
|
||||
shape_feature = ShapelyFeature(ob.getGeometry(),ccrs.PlateCarree(),
|
||||
facecolor='none', linestyle=":",edgecolor='#20B2AA')
|
||||
ax.add_feature(shape_feature)
|
||||
fig
|
||||
```
|
||||
|
||||
Using 758 river MultiLineStrings
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## Topography
|
||||
|
||||
Spatial envelopes are required for topo requests, which can become slow to download and render for large (CONUS) maps.
|
||||
|
||||
|
||||
```python
|
||||
import numpy.ma as ma
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
request.setDatatype("topo")
|
||||
request.addIdentifier("group", "/")
|
||||
request.addIdentifier("dataset", "full")
|
||||
request.setEnvelope(envelope)
|
||||
gridData = DataAccessLayer.getGridData(request)
|
||||
print(gridData)
|
||||
print("Number of grid records: " + str(len(gridData)))
|
||||
print("Sample grid data shape:\n" + str(gridData[0].getRawData().shape) + "\n")
|
||||
print("Sample grid data:\n" + str(gridData[0].getRawData()) + "\n")
|
||||
```
|
||||
|
||||
[<awips.dataaccess.PyGridData.PyGridData object at 0x1174adf50>]
|
||||
Number of grid records: 1
|
||||
Sample grid data shape:
|
||||
(778, 1058)
|
||||
|
||||
Sample grid data:
|
||||
[[ 1694. 1693. 1688. ..., 757. 761. 762.]
|
||||
[ 1701. 1701. 1701. ..., 758. 760. 762.]
|
||||
[ 1703. 1703. 1703. ..., 760. 761. 762.]
|
||||
...,
|
||||
[ 1767. 1741. 1706. ..., 769. 762. 768.]
|
||||
[ 1767. 1746. 1716. ..., 775. 765. 761.]
|
||||
[ 1781. 1753. 1730. ..., 766. 762. 759.]]
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
grid=gridData[0]
|
||||
topo=ma.masked_invalid(grid.getRawData())
|
||||
lons, lats = grid.getLatLonCoords()
|
||||
print(topo.min())
|
||||
print(topo.max())
|
||||
|
||||
# Plot topography
|
||||
cs = ax.contourf(lons, lats, topo, 80, cmap=plt.get_cmap('terrain'),alpha=0.1)
|
||||
cbar = fig.colorbar(cs, extend='both', shrink=0.5, orientation='horizontal')
|
||||
cbar.set_label("topography height in meters")
|
||||
|
||||
fig
|
||||
```
|
||||
|
||||
623.0
|
||||
4328.0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
ok
|
||||
|
||||
## mapdata.airport
|
||||
|
||||
|
|
234
docs/python/model-sounding-data.md
Normal file
|
@ -0,0 +1,234 @@
|
|||
|
||||
The EDEX modelsounding plugin creates 64-level vertical profiles from GFS and ETA (NAM) BUFR products distirubted over NOAAport. Paramters which are requestable are **pressure**, **temperature**, **specHum**, **uComp**, **vComp**, **omega**, **cldCvr**.
|
||||
|
||||
|
||||
```python
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
import matplotlib.tri as mtri
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
||||
from math import exp, log
|
||||
import numpy as np
|
||||
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
request.setDatatype("modelsounding")
|
||||
forecastModel = "GFS"
|
||||
request.addIdentifier("reportType", forecastModel)
|
||||
request.setParameters("pressure","temperature","specHum","uComp","vComp","omega","cldCvr")
|
||||
locations = DataAccessLayer.getAvailableLocationNames(request)
|
||||
locations.sort()
|
||||
list(locations)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
['CHE',
|
||||
'CRL',
|
||||
'EAX',
|
||||
'HSI',
|
||||
'KDSM',
|
||||
'KFOE',
|
||||
'KFRM',
|
||||
'KFSD',
|
||||
'KGRI',
|
||||
'KLNK',
|
||||
'KMCI',
|
||||
'KMCW',
|
||||
'KMHE',
|
||||
'KMHK',
|
||||
'KMKC',
|
||||
'KOFK',
|
||||
'KOMA',
|
||||
'KRSL',
|
||||
'KSLN',
|
||||
'KSTJ',
|
||||
'KSUX',
|
||||
'KTOP',
|
||||
'KYKN',
|
||||
'OAX',
|
||||
'P#8',
|
||||
'P#9',
|
||||
'P#A',
|
||||
'P#G',
|
||||
'P#I',
|
||||
'RDD',
|
||||
'WSC']
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
request.setLocationNames("KMCI")
|
||||
cycles = DataAccessLayer.getAvailableTimes(request, True)
|
||||
times = DataAccessLayer.getAvailableTimes(request)
|
||||
|
||||
try:
|
||||
fcstRun = DataAccessLayer.getForecastRun(cycles[-1], times)
|
||||
list(fcstRun)
|
||||
response = DataAccessLayer.getGeometryData(request,[fcstRun[0]])
|
||||
except:
|
||||
print('No times available')
|
||||
exit
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
tmp,prs,sh = np.array([]),np.array([]),np.array([])
|
||||
uc,vc,om,cld = np.array([]),np.array([]),np.array([]),np.array([])
|
||||
|
||||
for ob in response:
|
||||
tmp = np.append(tmp,ob.getNumber("temperature"))
|
||||
prs = np.append(prs,ob.getNumber("pressure"))
|
||||
sh = np.append(sh,ob.getNumber("specHum"))
|
||||
uc = np.append(uc,ob.getNumber("uComp"))
|
||||
vc = np.append(vc,ob.getNumber("vComp"))
|
||||
om = np.append(om,ob.getNumber("omega"))
|
||||
cld = np.append(cld,ob.getNumber("cldCvr"))
|
||||
|
||||
print("parms = " + str(ob.getParameters()))
|
||||
print("site = " + str(ob.getLocationName()))
|
||||
print("geom = " + str(ob.getGeometry()))
|
||||
print("datetime = " + str(ob.getDataTime()))
|
||||
print("reftime = " + str(ob.getDataTime().getRefTime()))
|
||||
print("fcstHour = " + str(ob.getDataTime().getFcstTime()))
|
||||
print("period = " + str(ob.getDataTime().getValidPeriod()))
|
||||
sounding_title = forecastModel + " " + str(ob.getLocationName()) + "("+ str(ob.getGeometry())+")" + str(ob.getDataTime())
|
||||
```
|
||||
|
||||
parms = ['uComp', 'cldCvr', 'temperature', 'vComp', 'pressure', 'omega', 'specHum']
|
||||
site = KMCI
|
||||
geom = POINT (-94.72000122070312 39.31999969482422)
|
||||
datetime = 1970-01-18 04:45:50.400000 (0)
|
||||
reftime = Jan 18 70 04:45:50 GMT
|
||||
fcstHour = 0
|
||||
period = (Jan 18 70 04:45:50 , Jan 18 70 04:45:50 )
|
||||
|
||||
|
||||
## Create data arrays and calculate dewpoint from spec. humidity
|
||||
|
||||
|
||||
```python
|
||||
from metpy.calc import get_wind_components, lcl, dry_lapse, parcel_profile, dewpoint
|
||||
from metpy.calc import get_wind_speed,get_wind_dir, thermo, vapor_pressure
|
||||
from metpy.plots import SkewT, Hodograph
|
||||
from metpy.units import units, concatenate
|
||||
|
||||
# we can use units.* here...
|
||||
t = (tmp-273.15) * units.degC
|
||||
p = prs/100 * units.mbar
|
||||
|
||||
u,v = uc*1.94384,vc*1.94384 # m/s to knots
|
||||
spd = get_wind_speed(u, v) * units.knots
|
||||
dir = get_wind_dir(u, v) * units.deg
|
||||
```
|
||||
|
||||
## Dewpoint from Specific Humidity
|
||||
|
||||
Because the modelsounding plugin does not return dewpoint values, we must calculate the profile ourselves. Here are three examples of dewpoint calculated from specific humidity, including a manual calculation following NCEP AWIPS/NSHARP.
|
||||
|
||||
### 1) metpy calculated mixing ratio and vapor pressure
|
||||
|
||||
|
||||
```python
|
||||
from metpy.calc import get_wind_components, lcl, dry_lapse, parcel_profile, dewpoint
|
||||
from metpy.calc import get_wind_speed,get_wind_dir, thermo, vapor_pressure
|
||||
from metpy.plots import SkewT, Hodograph
|
||||
from metpy.units import units, concatenate
|
||||
|
||||
# we can use units.* here...
|
||||
t = (tmp-273.15) * units.degC
|
||||
p = prs/100 * units.mbar
|
||||
|
||||
u,v = uc*1.94384,vc*1.94384 # m/s to knots
|
||||
spd = get_wind_speed(u, v) * units.knots
|
||||
dir = get_wind_dir(u, v) * units.deg
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
rmix = (sh/(1-sh)) *1000 * units('g/kg')
|
||||
e = vapor_pressure(p, rmix)
|
||||
td = dewpoint(e)
|
||||
```
|
||||
|
||||
/Users/mj/miniconda2/lib/python2.7/site-packages/MetPy-0.3.0+34.gcf954c5-py2.7.egg/metpy/calc/thermo.py:371: RuntimeWarning: divide by zero encountered in log
|
||||
val = np.log(e / sat_pressure_0c)
|
||||
/Users/mj/miniconda2/lib/python2.7/site-packages/pint/quantity.py:1236: RuntimeWarning: divide by zero encountered in log
|
||||
out = uf(*mobjs)
|
||||
/Users/mj/miniconda2/lib/python2.7/site-packages/pint/quantity.py:693: RuntimeWarning: invalid value encountered in true_divide
|
||||
magnitude = magnitude_op(self._magnitude, other_magnitude)
|
||||
|
||||
|
||||
### 2) metpy calculated assuming spec. humidity = mixing ratio
|
||||
|
||||
|
||||
```python
|
||||
td2 = dewpoint(vapor_pressure(p, sh))
|
||||
```
|
||||
|
||||
### 3) NCEP AWIPS soundingrequest plugin
|
||||
based on GEMPAK/NSHARP, from https://github.com/Unidata/awips2-ncep/blob/unidata_16.2.2/edex/gov.noaa.nws.ncep.edex.plugin.soundingrequest/src/gov/noaa/nws/ncep/edex/plugin/soundingrequest/handler/MergeSounding.java#L1783
|
||||
|
||||
|
||||
```python
|
||||
# new arrays
|
||||
ntmp = tmp
|
||||
|
||||
# where p=pressure(pa), T=temp(C), T0=reference temp(273.16)
|
||||
rh = 0.263*prs*sh / (np.exp(17.67*ntmp/(ntmp+273.15-29.65)))
|
||||
vaps = 6.112 * np.exp((17.67 * ntmp) / (ntmp + 243.5))
|
||||
vapr = rh * vaps / 100
|
||||
dwpc = np.array(243.5 * (np.log(6.112) - np.log(vapr)) / (np.log(vapr) - np.log(6.112) - 17.67)) * units.degC
|
||||
```
|
||||
|
||||
/Users/mj/miniconda2/lib/python2.7/site-packages/ipykernel/__main__.py:8: RuntimeWarning: divide by zero encountered in log
|
||||
/Users/mj/miniconda2/lib/python2.7/site-packages/ipykernel/__main__.py:8: RuntimeWarning: invalid value encountered in divide
|
||||
|
||||
|
||||
## Plot with MetPy
|
||||
|
||||
|
||||
```python
|
||||
%matplotlib inline
|
||||
|
||||
plt.rcParams['figure.figsize'] = (12, 14)
|
||||
|
||||
# Create a skewT plot
|
||||
skew = SkewT()
|
||||
|
||||
# Plot the data
|
||||
skew.plot(p, t, 'r', linewidth=2)
|
||||
skew.plot(p, td, 'b', linewidth=2)
|
||||
skew.plot(p, td2, 'y')
|
||||
skew.plot(p, dwpc, 'g', linewidth=2)
|
||||
|
||||
skew.plot_barbs(p, u, v)
|
||||
skew.ax.set_ylim(1000, 100)
|
||||
skew.ax.set_xlim(-40, 60)
|
||||
|
||||
plt.title(sounding_title)
|
||||
|
||||
# Calculate LCL height and plot as black dot
|
||||
l = lcl(p[0], t[0], td[0])
|
||||
lcl_temp = dry_lapse(concatenate((p[0], l)), t[0])[-1].to('degC')
|
||||
skew.plot(l, lcl_temp, 'ko', markerfacecolor='black')
|
||||
|
||||
|
||||
# An example of a slanted line at constant T -- in this case the 0 isotherm
|
||||
l = skew.ax.axvline(0, color='c', linestyle='--', linewidth=2)
|
||||
|
||||
# Draw hodograph
|
||||
ax_hod = inset_axes(skew.ax, '40%', '40%', loc=2)
|
||||
h = Hodograph(ax_hod, component_range=get_wind_speed(u, v).max())
|
||||
h.add_grid(increment=20)
|
||||
h.plot_colormapped(u, v, spd)
|
||||
|
||||
# Show the plot
|
||||
plt.show()
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
275
docs/python/nexrad-level-3-radar.md
Normal file
|
@ -0,0 +1,275 @@
|
|||
|
||||
Shown here are plots for Base Reflectivity (N0Q, 94) and Base Velocity (N0U, 99) using AWIPS data rendered with Matplotlib, Cartopy, and MetPy. This example improves upon existing Level 3 Python rendering by doing the following:
|
||||
|
||||
* Display scaled and labeled colorbar below each figure.
|
||||
* Plot radar radial images as coordinate maps in Cartopy and label with lat/lon.
|
||||
* 8 bit Z and V colormap and data scaling added to MetPy from operational AWIPS.
|
||||
* Level 3 data are retrieved from the [Unidata EDEX Cloud server](http://unidata.github.io/awips2/docs/install/install-cave.html#how-to-run-cave) (`edex-cloud.unidata.ucar.edu`)
|
||||
* Raw HDF5 byte data are converted to product values and scaled according to (page 3-34 https://www.roc.noaa.gov/wsr88d/PublicDocs/ICDS/2620001U.pdf)
|
||||
|
||||
The threshold level fields are used to describe (up to) 256 levels as follows:
|
||||
halfword 31 contains the minimum data value in m/s*10 (or dBZ*10)
|
||||
halfword 32 contains the increment in m/s*10 (or dBZ*10)
|
||||
halfword 33 contains the number of levels (0 - 255)
|
||||
|
||||
According to the [ICD for the Product Specification](https://www.roc.noaa.gov/WSR88D/PublicDocs/NewTechnology/B17_2620003W_draft.pdf), *"the 256 data levels of the digital product cover a range of reflectivity between -32.0 to +94.5 dBZ, in increments of 0.5 dBZ. Level codes 0 and 1 correspond to 'Below Threshold' and 'Range Folded', respectively, while level codes 2 through 255 correspond to the reflectivity data itself"*.
|
||||
|
||||
So it's really 254 color values between -32 and +94.5 dBZ.
|
||||
|
||||
The ICD lists 16 specific color levels and directs 256-level reflectivity products to use corresponding colors, leaving it the rendering application to scale and blend between the 16 color values, and to make decisions about discrete color changes, apparently.
|
||||

|
||||
|
||||
For AWIPS, the National Weather Service uses a mostly-blended color scale with a discrete jump to red at reflectivity values of 50 dBZ:
|
||||
|
||||

|
||||
|
||||
50 dBZ corresponds to the 16-level color *light red* (**FF6060**). Note that `FF6060` is not used in the NWS AWIPS color scale, instead RGB value is given as `255,0,0` (hex code **FF0000**). 60 dBZ is not quite exactly where white starts, but it makes sense that it would. Obviously the AWIPS D2D authors took some liberties with their 256-level rendering, not adhering strictly to "dark red" for dBZ values between 60-65 (white was for 70 dBZ and above on the 16-level colormap). For this exercise we will assume 50 dBZ should be red and 60 dBZ white, and 75 dBZ cyan.
|
||||
|
||||
|
||||
### Setup
|
||||
|
||||
> pip install python-awips matplotlib cartopy metpy
|
||||
|
||||
### Python Script
|
||||
|
||||
Download this script as a [Jupyter Notebook](http://nbviewer.jupyter.org/github/Unidata/python-awips/blob/master/examples/notebooks/NEXRAD_Level_3_Plot_with_Matplotlib.ipynb).
|
||||
|
||||
|
||||
```python
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
from awips import ThriftClient, RadarCommon
|
||||
from dynamicserialize.dstypes.com.raytheon.uf.common.time import TimeRange
|
||||
from dynamicserialize.dstypes.com.raytheon.uf.common.dataplugin.radar.request import GetRadarDataRecordRequest
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from numpy import ma
|
||||
from metpy.plots import ctables
|
||||
import cartopy.crs as ccrs
|
||||
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
|
||||
|
||||
# set EDEX server and radar site definitions
|
||||
site = 'kmux'
|
||||
DataAccessLayer.changeEDEXHost('edex-cloud.unidata.ucar.edu')
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
request.setDatatype('radar')
|
||||
request.setLocationNames(site)
|
||||
|
||||
# Get latest time for site
|
||||
datatimes = DataAccessLayer.getAvailableTimes(request)
|
||||
dateTimeStr = str(datatimes[-1])
|
||||
dateTimeStr = "2017-02-02 03:53:03"
|
||||
buffer = 60 # seconds
|
||||
dateTime = datetime.strptime(dateTimeStr, '%Y-%m-%d %H:%M:%S')
|
||||
# Build timerange +/- buffer
|
||||
beginRange = dateTime - timedelta(0, buffer)
|
||||
endRange = dateTime + timedelta(0, buffer)
|
||||
timerange = TimeRange(beginRange, endRange)
|
||||
|
||||
# GetRadarDataRecordRequest to query site with timerange
|
||||
client = ThriftClient.ThriftClient('edex-cloud.unidata.ucar.edu')
|
||||
request = GetRadarDataRecordRequest()
|
||||
request.setTimeRange(timerange)
|
||||
request.setRadarId(site)
|
||||
|
||||
# Map config
|
||||
def make_map(bbox, projection=ccrs.PlateCarree()):
|
||||
fig, ax = plt.subplots(figsize=(12, 12),
|
||||
subplot_kw=dict(projection=projection))
|
||||
ax.set_extent(bbox)
|
||||
ax.coastlines(resolution='50m')
|
||||
gl = ax.gridlines(draw_labels=True)
|
||||
gl.xlabels_top = gl.ylabels_right = False
|
||||
gl.xformatter = LONGITUDE_FORMATTER
|
||||
gl.yformatter = LATITUDE_FORMATTER
|
||||
return fig, ax
|
||||
|
||||
# ctable defines the colortable, beginning value, data increment
|
||||
# * For N0Q the scale is -20 to +75 dBZ in increments of 0.5 dBZ
|
||||
# * For N0U the scale is -100 to +100 kts in increments of 1 kt
|
||||
nexrad = {}
|
||||
nexrad["N0Q"] = {
|
||||
'id': 94,
|
||||
'unit':'dBZ',
|
||||
'name':'0.5 deg Base Reflectivity',
|
||||
'ctable': ['NWSStormClearReflectivity',-20., 0.5],
|
||||
'res': 1000.,
|
||||
'elev': '0.5'
|
||||
}
|
||||
nexrad["N0U"] = {
|
||||
'id': 99,
|
||||
'unit':'kts',
|
||||
'name':'0.5 deg Base Velocity',
|
||||
'ctable': ['NWS8bitVel',-100.,1.],
|
||||
'res': 250.,
|
||||
'elev': '0.5'
|
||||
}
|
||||
grids = []
|
||||
for code in nexrad:
|
||||
request.setProductCode(nexrad[code]['id'])
|
||||
request.setPrimaryElevationAngle(nexrad[code]['elev'])
|
||||
response = client.sendRequest(request)
|
||||
|
||||
if response.getData():
|
||||
for record in response.getData():
|
||||
# Get record hdf5 data
|
||||
idra = record.getHdf5Data()
|
||||
rdat,azdat,depVals,threshVals = RadarCommon.get_hdf5_data(idra)
|
||||
dim = rdat.getDimension()
|
||||
lat,lon = float(record.getLatitude()),float(record.getLongitude())
|
||||
radials,rangeGates = rdat.getSizes()
|
||||
|
||||
# Convert raw byte to pixel value
|
||||
rawValue=np.array(rdat.getByteData())
|
||||
array = []
|
||||
for rec in rawValue:
|
||||
if rec<0:
|
||||
rec+=256
|
||||
array.append(rec)
|
||||
|
||||
if azdat:
|
||||
azVals = azdat.getFloatData()
|
||||
az = np.array(RadarCommon.encode_radial(azVals))
|
||||
dattyp = RadarCommon.get_data_type(azdat)
|
||||
az = np.append(az,az[-1])
|
||||
|
||||
header = RadarCommon.get_header(record, format, rangeGates, radials, azdat, 'description')
|
||||
rng = np.linspace(0, rangeGates, rangeGates + 1)
|
||||
|
||||
# Convert az/range to a lat/lon
|
||||
from pyproj import Geod
|
||||
g = Geod(ellps='clrk66')
|
||||
center_lat = np.ones([len(az),len(rng)])*lat
|
||||
center_lon = np.ones([len(az),len(rng)])*lon
|
||||
az2D = np.ones_like(center_lat)*az[:,None]
|
||||
rng2D = np.ones_like(center_lat)*np.transpose(rng[:,None])*nexrad[code]['res']
|
||||
lons,lats,back=g.fwd(center_lon,center_lat,az2D,rng2D)
|
||||
bbox = [lons.min(), lons.max(), lats.min(), lats.max()]
|
||||
|
||||
# Create 2d array
|
||||
multiArray = np.reshape(array, (-1, rangeGates))
|
||||
data = ma.array(multiArray)
|
||||
|
||||
# threshVals[0:2] contains halfwords 31,32,33 (min value, increment, num levels)
|
||||
data = ma.array(threshVals[0]/10. + (multiArray)*threshVals[1]/10.)
|
||||
|
||||
if nexrad[code]['unit'] == 'kts':
|
||||
data[data<-63] = ma.masked
|
||||
data *= 1.94384 # Convert to knots
|
||||
else:
|
||||
data[data<=((threshVals[0]/10.)+threshVals[1]/10.)] = ma.masked
|
||||
|
||||
# Save our requested grids so we can render them multiple times
|
||||
product = {
|
||||
"code": code,
|
||||
"bbox": bbox,
|
||||
"lats": lats,
|
||||
"lons": lons,
|
||||
"data": data
|
||||
}
|
||||
grids.append(product)
|
||||
|
||||
print("Processed "+str(len(grids))+" grids.")
|
||||
```
|
||||
|
||||
Processed 2 grids.
|
||||
|
||||
|
||||
## Plot N0Q and N0U with Cartopy
|
||||
|
||||
|
||||
```python
|
||||
for rec in grids:
|
||||
code = rec["code"]
|
||||
bbox = rec["bbox"]
|
||||
lats = rec["lats"]
|
||||
lons = rec["lons"]
|
||||
data = rec["data"]
|
||||
# Create figure
|
||||
%matplotlib inline
|
||||
fig, ax = make_map(bbox=bbox)
|
||||
# Colortable filename, beginning value, increment
|
||||
ctable = nexrad[code]['ctable'][0]
|
||||
beg = nexrad[code]['ctable'][1]
|
||||
inc = nexrad[code]['ctable'][2]
|
||||
|
||||
norm, cmap = ctables.registry.get_with_steps(ctable, beg, inc)
|
||||
cs = ax.pcolormesh(lons, lats, data, norm=norm, cmap=cmap)
|
||||
ax.set_aspect('equal', 'datalim')
|
||||
|
||||
cbar = plt.colorbar(cs, extend='both', shrink=0.75, orientation='horizontal')
|
||||
cbar.set_label(site.upper()+" "+ str(nexrad[code]['res']/1000.) +"km " \
|
||||
+nexrad[code]['name']+" ("+code+") " \
|
||||
+nexrad[code]['unit']+" " \
|
||||
+str(record.getDataTime()))
|
||||
|
||||
# Zoom to within +-2 deg of center
|
||||
ax.set_xlim(lon-2., lon+2.)
|
||||
ax.set_ylim(lat-2., lat+2.)
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
compare with the same product scan rendered in AWIPS CAVE (slightly different projections and still some color mapping differences, most noticeable in ground clutter).
|
||||
|
||||

|
||||
|
||||
# Two-panel plot, zoomed in
|
||||
|
||||
|
||||
```python
|
||||
fig, axes = plt.subplots(ncols=2,figsize=(12,9),
|
||||
subplot_kw=dict(projection=ccrs.PlateCarree()))
|
||||
i=0
|
||||
for rec,ax in zip(grids, axes):
|
||||
|
||||
code = rec["code"]
|
||||
bbox = rec["bbox"]
|
||||
lats = rec["lats"]
|
||||
lons = rec["lons"]
|
||||
data = rec["data"]
|
||||
|
||||
# Create figure
|
||||
ax.set_extent(bbox)
|
||||
ax.coastlines(resolution='50m')
|
||||
gl = ax.gridlines(draw_labels=True)
|
||||
gl.xlabels_top = gl.ylabels_right = False
|
||||
if i>0: gl.ylabels_left = False # hide right-pane left axis label
|
||||
gl.xformatter = LONGITUDE_FORMATTER
|
||||
gl.yformatter = LATITUDE_FORMATTER
|
||||
|
||||
# Colortable filename, beginning value, increment
|
||||
colorvals=nexrad[code]['ctable']
|
||||
ctable = nexrad[code]['ctable'][0]
|
||||
beg = nexrad[code]['ctable'][1]
|
||||
inc = nexrad[code]['ctable'][2]
|
||||
|
||||
norm, cmap = ctables.registry.get_with_steps(ctable, beg, inc)
|
||||
cs = ax.pcolormesh(lons, lats, data, norm=norm, cmap=cmap)
|
||||
ax.set_aspect('equal', 'datalim')
|
||||
cbar = fig.colorbar(cs, orientation='horizontal', ax=ax)
|
||||
cbar.set_label(site.upper()+" "+code+" "+nexrad[code]['unit']+" "+str(record.getDataTime()))
|
||||
plt.tight_layout()
|
||||
|
||||
# Zoom
|
||||
ax.set_xlim(lon-.1, lon+.1)
|
||||
ax.set_ylim(lat-.1, lat+.1)
|
||||
i+=1
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
and again compared to CAVE
|
||||
|
||||

|
|
@ -21,21 +21,738 @@ The [python-awips](https://python-awips.readthedocs.io/en/latest/) package provi
|
|||
|
||||
## Example
|
||||
|
||||
The simplest example requesting the RAP40 surface temperature grid from a remote EDEX server, saved to 2-dimensional Numpy arrays named `data`, `lons`, and `lats`.
|
||||
This examples covers the callable methods of the Python AWIPS DAF when working with gridded data. We start with a connection to an EDEX server, then query data types, then grid names, parameters, levels, and other information. Finally the gridded data is plotted for its domain using Matplotlib and Cartopy.
|
||||
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
dataTypes = DataAccessLayer.getSupportedDatatypes()
|
||||
request.setDatatype("grid")
|
||||
request.addLocationNames("RAP40")
|
||||
request.setParameters("T")
|
||||
request.setLevels("0.0SFC")
|
||||
cycles = DataAccessLayer.getAvailableTimes(request, True)
|
||||
times = DataAccessLayer.getAvailableTimes(request)
|
||||
response = DataAccessLayer.getGridData(request, times[-1])
|
||||
for grid in response:
|
||||
data = grid.getRawData()
|
||||
lons, lats = grid.getLatLonCoords()
|
||||
### DataAccessLayer.changeEDEXHost()
|
||||
|
||||
After DataAccessLayer is imported from the package `awips.dataaccess`, the first step is to define the EDEX data server hostname (`edex-cloud.unidata.ucar.edu` for these examples)
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
```
|
||||
|
||||
### DataAccessLayer.getSupportedDatatypes()
|
||||
|
||||
getSupportedDatatypes() returns a list of available data types offered by the EDEX server defined above.
|
||||
|
||||
|
||||
```python
|
||||
dataTypes = DataAccessLayer.getSupportedDatatypes()
|
||||
list(dataTypes)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
['acars',
|
||||
'airep',
|
||||
'binlightning',
|
||||
'bufrmosavn',
|
||||
'bufrmoseta',
|
||||
'bufrmosgfs',
|
||||
'bufrmoshpc',
|
||||
'bufrmoslamp',
|
||||
'bufrmosmrf',
|
||||
'bufrua',
|
||||
'climate',
|
||||
'common_obs_spatial',
|
||||
'ffmp',
|
||||
'gfe',
|
||||
'grid',
|
||||
'hydro',
|
||||
'ldadmesonet',
|
||||
'maps',
|
||||
'modelsounding',
|
||||
'obs',
|
||||
'pirep',
|
||||
'practicewarning',
|
||||
'profiler',
|
||||
'radar',
|
||||
'radar_spatial',
|
||||
'satellite',
|
||||
'sfcobs',
|
||||
'topo',
|
||||
'warning']
|
||||
|
||||
|
||||
|
||||
### DataAccessLayer.newDataRequest()
|
||||
|
||||
Now create a new data request, and set the data type to **grid** and "locationName" to **RAP40** with **setDataType()** and **setLocationNames()**
|
||||
|
||||
|
||||
```python
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
request.setDatatype("grid")
|
||||
```
|
||||
|
||||
### DataAccessLayer.getAvailableLocationNames()
|
||||
|
||||
With datatype set to "grid", we can query all available grid names with **getAvailableLocationNames()**
|
||||
|
||||
|
||||
```python
|
||||
available_grids = DataAccessLayer.getAvailableLocationNames(request)
|
||||
available_grids.sort()
|
||||
list(available_grids)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
['CMC',
|
||||
'EKDMOS',
|
||||
'EKDMOS-AK',
|
||||
'ESTOFS',
|
||||
'ETSS',
|
||||
'FFG-ALR',
|
||||
'FFG-FWR',
|
||||
'FFG-KRF',
|
||||
'FFG-MSR',
|
||||
'FFG-ORN',
|
||||
'FFG-PTR',
|
||||
'FFG-RHA',
|
||||
'FFG-RSA',
|
||||
'FFG-STR',
|
||||
'FFG-TAR',
|
||||
'FFG-TIR',
|
||||
'FFG-TUA',
|
||||
'FNMOC-FAROP',
|
||||
'FNMOC-NCODA',
|
||||
'FNMOC-WW3',
|
||||
'FNMOC-WW3-Europe',
|
||||
'GFS',
|
||||
'GFS20',
|
||||
'GFSLAMP5',
|
||||
'GribModel:9:151:172',
|
||||
'HFR-EAST_6KM',
|
||||
'HFR-EAST_PR_6KM',
|
||||
'HFR-US_EAST_DELAWARE_1KM',
|
||||
'HFR-US_EAST_FLORIDA_2KM',
|
||||
'HFR-US_EAST_NORTH_2KM',
|
||||
'HFR-US_EAST_SOUTH_2KM',
|
||||
'HFR-US_EAST_VIRGINIA_1KM',
|
||||
'HFR-US_HAWAII_1KM',
|
||||
'HFR-US_HAWAII_2KM',
|
||||
'HFR-US_HAWAII_6KM',
|
||||
'HFR-US_WEST_500M',
|
||||
'HFR-US_WEST_CENCAL_2KM',
|
||||
'HFR-US_WEST_LOSANGELES_1KM',
|
||||
'HFR-US_WEST_LOSOSOS_1KM',
|
||||
'HFR-US_WEST_NORTH_2KM',
|
||||
'HFR-US_WEST_SANFRAN_1KM',
|
||||
'HFR-US_WEST_SOCAL_2KM',
|
||||
'HFR-US_WEST_WASHINGTON_1KM',
|
||||
'HFR-WEST_6KM',
|
||||
'HPCGuide',
|
||||
'HPCqpf',
|
||||
'HPCqpfNDFD',
|
||||
'HRRR',
|
||||
'LAMP2p5',
|
||||
'MOSGuide',
|
||||
'MPE-Local-ALR',
|
||||
'MPE-Local-FWR',
|
||||
'MPE-Local-MSR',
|
||||
'MPE-Local-ORN',
|
||||
'MPE-Local-RHA',
|
||||
'MPE-Local-SJU',
|
||||
'MPE-Local-STR',
|
||||
'MPE-Local-TAR',
|
||||
'MPE-Local-TIR',
|
||||
'MPE-Mosaic-ALR',
|
||||
'MPE-Mosaic-FWR',
|
||||
'MPE-Mosaic-MSR',
|
||||
'MPE-Mosaic-ORN',
|
||||
'MPE-Mosaic-RHA',
|
||||
'MPE-Mosaic-SJU',
|
||||
'MPE-Mosaic-TAR',
|
||||
'MPE-Mosaic-TIR',
|
||||
'NAM12',
|
||||
'NAM40',
|
||||
'NAVGEM',
|
||||
'NCWF',
|
||||
'NDFD',
|
||||
'NOHRSC-SNOW',
|
||||
'NamDNG',
|
||||
'PROB3HR',
|
||||
'QPE-ALR',
|
||||
'QPE-Auto-TUA',
|
||||
'QPE-FWR',
|
||||
'QPE-KRF',
|
||||
'QPE-MSR',
|
||||
'QPE-Manual-KRF',
|
||||
'QPE-ORN',
|
||||
'QPE-RFC-PTR',
|
||||
'QPE-RFC-RSA',
|
||||
'QPE-RFC-STR',
|
||||
'QPE-TIR',
|
||||
'QPE-TUA',
|
||||
'QPE-XNAV-ALR',
|
||||
'QPE-XNAV-FWR',
|
||||
'QPE-XNAV-KRF',
|
||||
'QPE-XNAV-MSR',
|
||||
'QPE-XNAV-ORN',
|
||||
'QPE-XNAV-SJU',
|
||||
'QPE-XNAV-TAR',
|
||||
'QPE-XNAV-TIR',
|
||||
'QPE-XNAV-TUA',
|
||||
'RAP13',
|
||||
'RAP20',
|
||||
'RAP40',
|
||||
'RFCqpf',
|
||||
'RTMA',
|
||||
'RTOFS-Now-WestAtl',
|
||||
'RTOFS-Now-WestConus',
|
||||
'RTOFS-WestAtl',
|
||||
'RTOFS-WestConus',
|
||||
'SeaIce',
|
||||
'TPCWindProb',
|
||||
'UKMET-MODEL1',
|
||||
'URMA25',
|
||||
'WaveWatch']
|
||||
|
||||
|
||||
|
||||
### Set grid name with `setLocationNames()`
|
||||
|
||||
|
||||
```python
|
||||
request.setLocationNames("RAP13")
|
||||
```
|
||||
|
||||
# List Available Parameters for a Grid
|
||||
|
||||
### DataAccessLayer.getAvailableParameters()
|
||||
|
||||
After datatype and model name (locationName) are set, you can query all available parameters with **getAvailableParameters()**
|
||||
|
||||
|
||||
```python
|
||||
availableParms = DataAccessLayer.getAvailableParameters(request)
|
||||
availableParms.sort()
|
||||
list(availableParms)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
['0to5',
|
||||
'2xTP6hr',
|
||||
'AV',
|
||||
'Along',
|
||||
'AppT',
|
||||
'BLI',
|
||||
'BlkMag',
|
||||
'BlkShr',
|
||||
'CAPE',
|
||||
'CFRZR',
|
||||
'CICEP',
|
||||
'CIn',
|
||||
'CP',
|
||||
'CP1hr',
|
||||
'CPr',
|
||||
'CPrD',
|
||||
'CRAIN',
|
||||
'CSNOW',
|
||||
'CURU',
|
||||
'CXR',
|
||||
'CapeStk',
|
||||
'Corf',
|
||||
'CorfF',
|
||||
'CorfFM',
|
||||
'CorfM',
|
||||
'CritT1',
|
||||
'DIABi',
|
||||
'DivF',
|
||||
'DivFn',
|
||||
'DivFs',
|
||||
'DpD',
|
||||
'DpDt',
|
||||
'DpT',
|
||||
'Dpress',
|
||||
'DthDt',
|
||||
'EHI',
|
||||
'EHI01',
|
||||
'EHIi',
|
||||
'EPT',
|
||||
'EPTA',
|
||||
'EPTC',
|
||||
'EPTGrd',
|
||||
'EPTGrdM',
|
||||
'EPTs',
|
||||
'EPVg',
|
||||
'EPVs',
|
||||
'EPVt1',
|
||||
'EPVt2',
|
||||
'FVecs',
|
||||
'FeatMot',
|
||||
'FnVecs',
|
||||
'FsVecs',
|
||||
'Fzra1',
|
||||
'Fzra2',
|
||||
'GH',
|
||||
'GHxSM',
|
||||
'GHxSM2',
|
||||
'Gust',
|
||||
'HI',
|
||||
'HI1',
|
||||
'HI3',
|
||||
'HI4',
|
||||
'HIdx',
|
||||
'HPBL',
|
||||
'Heli',
|
||||
'Into',
|
||||
'KI',
|
||||
'L-I',
|
||||
'LIsfc2x',
|
||||
'LgSP1hr',
|
||||
'MAdv',
|
||||
'MCon',
|
||||
'MCon2',
|
||||
'MMSP',
|
||||
'MSFDi',
|
||||
'MSFi',
|
||||
'MSFmi',
|
||||
'MSG',
|
||||
'MTV',
|
||||
'Mix1',
|
||||
'Mix2',
|
||||
'Mmag',
|
||||
'MnT',
|
||||
'MpV',
|
||||
'MxT',
|
||||
'NBE',
|
||||
'NetIO',
|
||||
'OmDiff',
|
||||
'P',
|
||||
'PAdv',
|
||||
'PBE',
|
||||
'PFrnt',
|
||||
'PGrd',
|
||||
'PGrd1',
|
||||
'PGrdM',
|
||||
'PIVA',
|
||||
'PR',
|
||||
'PTvA',
|
||||
'PTyp',
|
||||
'PVV',
|
||||
'PW',
|
||||
'PW2',
|
||||
'PoT',
|
||||
'PoTA',
|
||||
'QPV1',
|
||||
'QPV2',
|
||||
'QPV3',
|
||||
'QPV4',
|
||||
'REFC',
|
||||
'RH',
|
||||
'RH_001_bin',
|
||||
'RH_002_bin',
|
||||
'RM5',
|
||||
'RMGH2',
|
||||
'RRtype',
|
||||
'RV',
|
||||
'Rain1',
|
||||
'Rain2',
|
||||
'Rain3',
|
||||
'Ro',
|
||||
'SH',
|
||||
'SHx',
|
||||
'SLI',
|
||||
'SNW',
|
||||
'SNWA',
|
||||
'SRMm',
|
||||
'SRMmM',
|
||||
'SSi',
|
||||
'Shear',
|
||||
'ShrMag',
|
||||
'SnD',
|
||||
'Snow1',
|
||||
'Snow2',
|
||||
'Snow3',
|
||||
'SnowT',
|
||||
'St-Pr',
|
||||
'St-Pr1hr',
|
||||
'StrTP',
|
||||
'StrmMot',
|
||||
'T',
|
||||
'TAdv',
|
||||
'TGrd',
|
||||
'TGrdM',
|
||||
'TP',
|
||||
'TP12hr',
|
||||
'TP168hr',
|
||||
'TP1hr',
|
||||
'TP24hr',
|
||||
'TP36hr',
|
||||
'TP3hr',
|
||||
'TP48hr',
|
||||
'TP6hr',
|
||||
'TP72hr',
|
||||
'TPrun',
|
||||
'TPx12x6',
|
||||
'TPx1x3',
|
||||
'TQIND',
|
||||
'TV',
|
||||
'TW',
|
||||
'T_001_bin',
|
||||
'Tdef',
|
||||
'Tdend',
|
||||
'ThGrd',
|
||||
'TmDpD',
|
||||
'Tmax',
|
||||
'Tmin',
|
||||
'TotQi',
|
||||
'Tstk',
|
||||
'TwMax',
|
||||
'TwMin',
|
||||
'Twstk',
|
||||
'TxSM',
|
||||
'USTM',
|
||||
'VAdv',
|
||||
'VAdvAdvection',
|
||||
'VSTM',
|
||||
'Vis',
|
||||
'WD',
|
||||
'WEASD',
|
||||
'WEASD1hr',
|
||||
'WGS',
|
||||
'Wind',
|
||||
'WndChl',
|
||||
'ageoVC',
|
||||
'ageoW',
|
||||
'ageoWM',
|
||||
'cCape',
|
||||
'cCin',
|
||||
'cTOT',
|
||||
'capeToLvl',
|
||||
'dCape',
|
||||
'dGH12',
|
||||
'dP',
|
||||
'dP1hr',
|
||||
'dP3hr',
|
||||
'dP6hr',
|
||||
'dPW1hr',
|
||||
'dPW3hr',
|
||||
'dPW6hr',
|
||||
'dT',
|
||||
'dVAdv',
|
||||
'dZ',
|
||||
'defV',
|
||||
'del2gH',
|
||||
'df',
|
||||
'fGen',
|
||||
'fnD',
|
||||
'fsD',
|
||||
'gamma',
|
||||
'gammaE',
|
||||
'geoVort',
|
||||
'geoW',
|
||||
'geoWM',
|
||||
'mixRat',
|
||||
'msl-P',
|
||||
'muCape',
|
||||
'pV',
|
||||
'pVeq',
|
||||
'qDiv',
|
||||
'qVec',
|
||||
'qnVec',
|
||||
'qsVec',
|
||||
'shWlt',
|
||||
'snoRatCrocus',
|
||||
'snoRatEMCSREF',
|
||||
'snoRatSPC',
|
||||
'snoRatSPCdeep',
|
||||
'snoRatSPCsurface',
|
||||
'swtIdx',
|
||||
'tTOT',
|
||||
'tWind',
|
||||
'tWindU',
|
||||
'tWindV',
|
||||
'uFX',
|
||||
'uW',
|
||||
'vSmthW',
|
||||
'vTOT',
|
||||
'vW',
|
||||
'vertCirc',
|
||||
'wDiv',
|
||||
'wSp',
|
||||
'wSp_001_bin',
|
||||
'wSp_002_bin',
|
||||
'wSp_003_bin',
|
||||
'wSp_004_bin',
|
||||
'zAGL']
|
||||
|
||||
|
||||
|
||||
### setParameters()
|
||||
|
||||
|
||||
set the request parameter
|
||||
|
||||
|
||||
```python
|
||||
request.setParameters("T")
|
||||
```
|
||||
|
||||
## List Available Levels for Parameter
|
||||
|
||||
Using **DataAccessLayer.getAvailableLevels()**
|
||||
|
||||
|
||||
```python
|
||||
availableLevels = DataAccessLayer.getAvailableLevels(request)
|
||||
for level in availableLevels:
|
||||
print(level)
|
||||
```
|
||||
|
||||
0.0SFC
|
||||
350.0MB
|
||||
475.0MB
|
||||
225.0MB
|
||||
120.0_150.0BL
|
||||
900.0MB
|
||||
125.0MB
|
||||
450.0MB
|
||||
575.0MB
|
||||
325.0MB
|
||||
100.0MB
|
||||
1000.0MB
|
||||
60.0_90.0BL
|
||||
275.0MB
|
||||
1.0PV
|
||||
950.0MB
|
||||
150.0MB
|
||||
1.5PV
|
||||
700.0MB
|
||||
825.0MB
|
||||
150.0_180.0BL
|
||||
250.0MB
|
||||
375.0MB
|
||||
1000.0_500.0MB
|
||||
800.0MB
|
||||
925.0MB
|
||||
2.0PV
|
||||
0.5PV
|
||||
0.0TROP
|
||||
750.0MB
|
||||
500.0MB
|
||||
625.0MB
|
||||
400.0MB
|
||||
0.0FHAG
|
||||
2.0FHAG
|
||||
875.0MB
|
||||
175.0MB
|
||||
850.0MB
|
||||
600.0MB
|
||||
725.0MB
|
||||
975.0MB
|
||||
550.0MB
|
||||
675.0MB
|
||||
425.0MB
|
||||
200.0MB
|
||||
0.0_30.0BL
|
||||
30.0_60.0BL
|
||||
650.0MB
|
||||
525.0MB
|
||||
300.0MB
|
||||
90.0_120.0BL
|
||||
775.0MB
|
||||
0.0TILT
|
||||
0.5TILT
|
||||
340.0_350.0K
|
||||
290.0_300.0K
|
||||
700.0_600.0MB
|
||||
700.0_300.0MB
|
||||
320.0Ke
|
||||
800.0_750.0MB
|
||||
60.0TILT
|
||||
5.3TILT
|
||||
1000.0_900.0MB
|
||||
340.0K
|
||||
255.0K
|
||||
255.0_265.0K
|
||||
25.0TILT
|
||||
1000.0_850.0MB
|
||||
850.0_250.0MB
|
||||
280.0_290.0Ke
|
||||
320.0_330.0K
|
||||
310.0_320.0Ke
|
||||
310.0Ke
|
||||
330.0K
|
||||
900.0_800.0MB
|
||||
550.0_500.0MB
|
||||
2.4TILT
|
||||
50.0TILT
|
||||
35.0TILT
|
||||
12.0TILT
|
||||
300.0_310.0K
|
||||
0.9TILT
|
||||
320.0K
|
||||
400.0_350.0MB
|
||||
750.0_700.0MB
|
||||
345.0K
|
||||
250.0_260.0K
|
||||
300.0Ke
|
||||
290.0Ke
|
||||
950.0_900.0MB
|
||||
275.0_285.0Ke
|
||||
335.0Ke
|
||||
295.0_305.0Ke
|
||||
275.0_285.0K
|
||||
600.0_550.0MB
|
||||
310.0K
|
||||
335.0K
|
||||
700.0_500.0MB
|
||||
325.0_335.0K
|
||||
300.0K
|
||||
0.0MAXOMEGA
|
||||
315.0_325.0K
|
||||
325.0K
|
||||
340.0Ke
|
||||
300.0_250.0MB
|
||||
1.5TILT
|
||||
335.0_345.0K
|
||||
315.0K
|
||||
3.4TILT
|
||||
330.0Ke
|
||||
500.0_400.0MB
|
||||
305.0K
|
||||
285.0_295.0Ke
|
||||
14.0TILT
|
||||
325.0_335.0Ke
|
||||
850.0_800.0MB
|
||||
295.0Ke
|
||||
305.0Ke
|
||||
265.0_275.0K
|
||||
700.0_650.0MB
|
||||
450.0_400.0MB
|
||||
1.8TILT
|
||||
330.0_340.0K
|
||||
800.0_700.0MB
|
||||
850.0_300.0MB
|
||||
6.0TILT
|
||||
900.0_850.0MB
|
||||
320.0_330.0Ke
|
||||
8.7TILT
|
||||
650.0_600.0MB
|
||||
600.0_400.0MB
|
||||
55.0TILT
|
||||
270.0_280.0Ke
|
||||
30.0TILT
|
||||
310.0_320.0K
|
||||
1000.0_950.0MB
|
||||
250.0_200.0MB
|
||||
400.0_300.0MB
|
||||
500.0_100.0MB
|
||||
285.0Ke
|
||||
290.0K
|
||||
305.0_315.0K
|
||||
285.0_295.0K
|
||||
925.0_850.0MB
|
||||
275.0Ke
|
||||
300.0_200.0MB
|
||||
260.0_270.0K
|
||||
315.0_325.0Ke
|
||||
600.0_500.0MB
|
||||
16.7TILT
|
||||
280.0K
|
||||
500.0_250.0MB
|
||||
40.0TILT
|
||||
400.0_200.0MB
|
||||
300.0_310.0Ke
|
||||
270.0_280.0K
|
||||
1000.0_700.0MB
|
||||
45.0TILT
|
||||
850.0_500.0MB
|
||||
295.0K
|
||||
4.3TILT
|
||||
295.0_305.0K
|
||||
330.0_340.0Ke
|
||||
270.0K
|
||||
280.0_290.0K
|
||||
925.0_700.0MB
|
||||
260.0K
|
||||
10.0TILT
|
||||
325.0Ke
|
||||
285.0K
|
||||
290.0_300.0Ke
|
||||
7.5TILT
|
||||
280.0Ke
|
||||
500.0_450.0MB
|
||||
305.0_315.0Ke
|
||||
250.0K
|
||||
250.0_350.0K
|
||||
270.0Ke
|
||||
275.0K
|
||||
315.0Ke
|
||||
500.0_300.0MB
|
||||
350.0_300.0MB
|
||||
19.5TILT
|
||||
850.0_700.0MB
|
||||
350.0K
|
||||
265.0K
|
||||
0.0_0.0SFC
|
||||
|
||||
|
||||
* **0.0SFC** is the Surface level
|
||||
* **FHAG** stands for Fixed Height Above Ground (in meters)
|
||||
* **NTAT** stands for Nominal Top of the ATmosphere
|
||||
* **BL** stands for Boundary Layer, where **0.0_30.0BL** reads as *0-30 mb above ground level*
|
||||
* **TROP** is the Tropopause level
|
||||
|
||||
### request.setLevels()
|
||||
|
||||
For this example we will use Surface Temperature
|
||||
|
||||
|
||||
```python
|
||||
request.setLevels("2.0FHAG")
|
||||
```
|
||||
|
||||
### DataAccessLayer.getAvailableTimes()
|
||||
|
||||
* **getAvailableTimes(request, True)** will return an object of *run times* - formatted as `YYYY-MM-DD HH:MM:SS`
|
||||
* **getAvailableTimes(request)** will return an object of all times - formatted as `YYYY-MM-DD HH:MM:SS (F:ff)`
|
||||
* **getForecastRun(cycle, times)** will return a DataTime array for a single forecast cycle.
|
||||
|
||||
# Request a Grid
|
||||
|
||||
### DataAccessLayer.getGridData()
|
||||
|
||||
Now that we have our `request` and DataTime `fcstRun` arrays ready, it's time to request the data array from EDEX.
|
||||
|
||||
|
||||
```python
|
||||
cycles = DataAccessLayer.getAvailableTimes(request, True)
|
||||
times = DataAccessLayer.getAvailableTimes(request)
|
||||
fcstRun = DataAccessLayer.getForecastRun(cycles[-1], times)
|
||||
|
||||
response = DataAccessLayer.getGridData(request, [fcstRun[-1]])
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
for grid in response:
|
||||
data = grid.getRawData()
|
||||
lons, lats = grid.getLatLonCoords()
|
||||
print('Time :', str(grid.getDataTime()))
|
||||
|
||||
print('Model:', str(grid.getLocationName()))
|
||||
print('Parm :', str(grid.getParameter()))
|
||||
print('Unit :', str(grid.getUnit()))
|
||||
print(data.shape)
|
||||
print(data.min(), data.max())
|
||||
```
|
||||
|
||||
('Time :', '2017-08-14 14:00:00 (21)')
|
||||
('Model:', 'RAP13')
|
||||
('Parm :', 'T')
|
||||
('Unit :', 'K')
|
||||
(337, 451)
|
||||
(271.21939, 306.71939)
|
||||
|
||||
|
|
249
docs/python/satellite-imagery.md
Normal file
|
@ -0,0 +1,249 @@
|
|||
|
||||
Satellite images are returned by Python AWIPS as grids, and can be rendered with Cartopy pcolormesh the same as gridded forecast models in other python-awips examples.
|
||||
|
||||
|
||||
```python
|
||||
%matplotlib inline
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
import cartopy.crs as ccrs
|
||||
import cartopy.feature as cfeat
|
||||
import matplotlib.pyplot as plt
|
||||
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
|
||||
import numpy as np
|
||||
import datetime
|
||||
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
request.setDatatype("satellite")
|
||||
```
|
||||
|
||||
### Available Satellite Sectors and Products
|
||||
|
||||
|
||||
```python
|
||||
availableSectors = DataAccessLayer.getAvailableLocationNames(request)
|
||||
availableSectors.sort()
|
||||
|
||||
print("\nAvailable sectors and products\n")
|
||||
for sect in availableSectors:
|
||||
request.setLocationNames(sect)
|
||||
availableProducts = DataAccessLayer.getAvailableParameters(request)
|
||||
availableProducts.sort()
|
||||
print(sect + ":")
|
||||
for prod in availableProducts:
|
||||
print(" - "+prod)
|
||||
```
|
||||
|
||||
|
||||
Available sectors and products
|
||||
|
||||
* Alaska National:
|
||||
- Imager 11 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
- Percent of Normal TPW
|
||||
- Rain fall rate
|
||||
- Sounder Based Derived Precipitable Water (PW)
|
||||
* Alaska Regional:
|
||||
- Imager 11 micron IR
|
||||
- Imager 3.9 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
* East CONUS:
|
||||
- Imager 11 micron IR
|
||||
- Imager 13 micron (IR)
|
||||
- Imager 3.9 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
- Low cloud base imagery
|
||||
* GOES-East:
|
||||
- Imager 11 micron IR
|
||||
- Imager 13 micron IR
|
||||
- Imager 3.5-4.0 micron IR (Fog)
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
* GOES-East-West:
|
||||
- Imager 11 micron IR
|
||||
- Imager 13 micron IR
|
||||
- Imager 3.5-4.0 micron IR (Fog)
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
* GOES-Sounder:
|
||||
- CAPE
|
||||
- Sounder Based Derived Lifted Index (LI)
|
||||
- Sounder Based Derived Precipitable Water (PW)
|
||||
- Sounder Based Derived Surface Skin Temp (SFC Skin)
|
||||
- Sounder Based Total Column Ozone
|
||||
* GOES-West:
|
||||
- Imager 11 micron IR
|
||||
- Imager 13 micron IR
|
||||
- Imager 3.5-4.0 micron IR (Fog)
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
* Global:
|
||||
- Imager 11 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
* Hawaii National:
|
||||
- Gridded Cloud Amount
|
||||
- Gridded Cloud Top Pressure or Height
|
||||
- Imager 11 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
- Percent of Normal TPW
|
||||
- Rain fall rate
|
||||
- Sounder 11.03 micron imagery
|
||||
- Sounder 14.06 micron imagery
|
||||
- Sounder 3.98 micron imagery
|
||||
- Sounder 4.45 micron imagery
|
||||
- Sounder 6.51 micron imagery
|
||||
- Sounder 7.02 micron imagery
|
||||
- Sounder 7.43 micron imagery
|
||||
- Sounder Based Derived Lifted Index (LI)
|
||||
- Sounder Based Derived Precipitable Water (PW)
|
||||
- Sounder Based Derived Surface Skin Temp (SFC Skin)
|
||||
- Sounder Visible imagery
|
||||
* Hawaii Regional:
|
||||
- Imager 11 micron IR
|
||||
- Imager 13 micron (IR)
|
||||
- Imager 3.9 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
* Mollweide:
|
||||
- Imager 11 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
* NEXRCOMP:
|
||||
- DHR
|
||||
- DVL
|
||||
- EET
|
||||
- HHC
|
||||
- N0R
|
||||
- N1P
|
||||
- NTP
|
||||
* NH Composite - Meteosat-GOES E-GOES W-GMS:
|
||||
- Imager 11 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
* Northern Hemisphere Composite:
|
||||
- Imager 11 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
* Puerto Rico National:
|
||||
- Imager 11 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
- Percent of Normal TPW
|
||||
- Rain fall rate
|
||||
- Sounder Based Derived Precipitable Water (PW)
|
||||
* Puerto Rico Regional:
|
||||
- Imager 11 micron IR
|
||||
- Imager 13 micron (IR)
|
||||
- Imager 3.9 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
* Supernational:
|
||||
- Gridded Cloud Amount
|
||||
- Gridded Cloud Top Pressure or Height
|
||||
- Imager 11 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
- Percent of Normal TPW
|
||||
- Rain fall rate
|
||||
- Sounder Based Derived Lifted Index (LI)
|
||||
- Sounder Based Derived Precipitable Water (PW)
|
||||
- Sounder Based Derived Surface Skin Temp (SFC Skin)
|
||||
* West CONUS:
|
||||
- Imager 11 micron IR
|
||||
- Imager 13 micron (IR)
|
||||
- Imager 3.9 micron IR
|
||||
- Imager 6.7-6.5 micron IR (WV)
|
||||
- Imager Visible
|
||||
- Low cloud base imagery
|
||||
- Sounder 11.03 micron imagery
|
||||
- Sounder 14.06 micron imagery
|
||||
- Sounder 3.98 micron imagery
|
||||
- Sounder 4.45 micron imagery
|
||||
- Sounder 6.51 micron imagery
|
||||
- Sounder 7.02 micron imagery
|
||||
- Sounder 7.43 micron imagery
|
||||
- Sounder Visible imagery
|
||||
|
||||
|
||||
### Plot Global Water Vapor Composite
|
||||
|
||||
|
||||
```python
|
||||
request.setLocationNames("Global")
|
||||
availableProducts = DataAccessLayer.getAvailableParameters(request)
|
||||
availableProducts.sort()
|
||||
request.setParameters(availableProducts[0])
|
||||
|
||||
utc = datetime.datetime.utcnow()
|
||||
times = DataAccessLayer.getAvailableTimes(request)
|
||||
hourdiff = utc - datetime.datetime.strptime(str(times[-1]),'%Y-%m-%d %H:%M:%S')
|
||||
hours,days = hourdiff.seconds/3600,hourdiff.days
|
||||
minute = str((hourdiff.seconds - (3600 * hours)) / 60)
|
||||
offsetStr = ''
|
||||
if hours > 0:
|
||||
offsetStr += str(hours) + "hr "
|
||||
offsetStr += str(minute) + "m ago"
|
||||
if days > 1:
|
||||
offsetStr = str(days) + " days ago"
|
||||
|
||||
print("Found "+ str(len(times)) +" available times")
|
||||
print(" "+str(times[0]) + "\n to\n " + str(times[-1]))
|
||||
print("Using "+str(times[-1]) + " ("+offsetStr+")")
|
||||
```
|
||||
|
||||
> Found 96 available times
|
||||
> 2017-01-23 00:00:00
|
||||
> to
|
||||
> 2017-02-03 21:00:00
|
||||
> Using 2017-02-03 21:00:00 (2hr 3m ago)
|
||||
|
||||
|
||||
|
||||
```python
|
||||
response = DataAccessLayer.getGridData(request, [times[-1]])
|
||||
grid = response[0]
|
||||
data = grid.getRawData()
|
||||
lons,lats = grid.getLatLonCoords()
|
||||
bbox = [lons.min(), lons.max(), lats.min(), lats.max()]
|
||||
|
||||
print("grid size " + str(data.shape))
|
||||
print("grid extent " + str(list(bbox)))
|
||||
```
|
||||
|
||||
> grid size (1024, 2048)
|
||||
> grid extent [-179.91191, 179.99982, -89.977936, 89.890022]
|
||||
|
||||
|
||||
|
||||
```python
|
||||
def make_map(bbox, projection=ccrs.PlateCarree()):
|
||||
fig, ax = plt.subplots(figsize=(18,14),
|
||||
subplot_kw=dict(projection=projection))
|
||||
ax.set_extent(bbox)
|
||||
ax.coastlines(resolution='50m')
|
||||
gl = ax.gridlines(draw_labels=True)
|
||||
gl.xlabels_top = gl.ylabels_right = False
|
||||
gl.xformatter = LONGITUDE_FORMATTER
|
||||
gl.yformatter = LATITUDE_FORMATTER
|
||||
return fig, ax
|
||||
|
||||
fig, ax = make_map(bbox=bbox)
|
||||
# State boundaries
|
||||
states = cfeat.NaturalEarthFeature(category='cultural',
|
||||
name='admin_1_states_provinces_lines',
|
||||
scale='50m', facecolor='none')
|
||||
ax.add_feature(states, linestyle=':')
|
||||
cs = ax.pcolormesh(lons, lats, data, cmap='Greys_r')
|
||||
cbar = fig.colorbar(cs, shrink=0.9, orientation='horizontal')
|
||||
cbar.set_label(str(grid.getLocationName())+" " \
|
||||
+str(grid.getParameter())+" " \
|
||||
+str(grid.getDataTime().getRefTime()))
|
||||
plt.tight_layout()
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
205
docs/python/surface-obs-plot-metpy.md
Normal file
|
@ -0,0 +1,205 @@
|
|||
|
||||
Based on the MetPy example ["Station Plot with Layout"](http://metpy.readthedocs.org/en/latest/examples/generated/Station_Plot_with_Layout.html)
|
||||
|
||||
|
||||
```python
|
||||
import datetime
|
||||
import pandas
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pprint
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
from metpy.calc import get_wind_components
|
||||
from metpy.cbook import get_test_data
|
||||
from metpy.plots.wx_symbols import sky_cover, current_weather
|
||||
from metpy.plots import StationPlot, StationPlotLayout, simple_layout
|
||||
from metpy.units import units
|
||||
|
||||
def get_cloud_cover(code):
|
||||
if 'OVC' in code:
|
||||
return 1.0
|
||||
elif 'BKN' in code:
|
||||
return 6.0/8.0
|
||||
elif 'SCT' in code:
|
||||
return 4.0/8.0
|
||||
elif 'FEW' in code:
|
||||
return 2.0/8.0
|
||||
else:
|
||||
return 0
|
||||
|
||||
state_capital_wx_stations = {'Washington':'KOLM', 'Oregon':'KSLE', 'California':'KSAC',
|
||||
'Nevada':'KCXP', 'Idaho':'KBOI', 'Montana':'KHLN',
|
||||
'Utah':'KSLC', 'Arizona':'KDVT', 'New Mexico':'KSAF',
|
||||
'Colorado':'KBKF', 'Wyoming':'KCYS', 'North Dakota':'KBIS',
|
||||
'South Dakota':'KPIR', 'Nebraska':'KLNK', 'Kansas':'KTOP',
|
||||
'Oklahoma':'KPWA', 'Texas':'KATT', 'Louisiana':'KBTR',
|
||||
'Arkansas':'KLIT', 'Missouri':'KJEF', 'Iowa':'KDSM',
|
||||
'Minnesota':'KSTP', 'Wisconsin':'KMSN', 'Illinois':'KSPI',
|
||||
'Mississippi':'KHKS', 'Alabama':'KMGM', 'Nashville':'KBNA',
|
||||
'Kentucky':'KFFT', 'Indiana':'KIND', 'Michigan':'KLAN',
|
||||
'Ohio':'KCMH', 'Georgia':'KFTY', 'Florida':'KTLH',
|
||||
'South Carolina':'KCUB', 'North Carolina':'KRDU',
|
||||
'Virginia':'KRIC', 'West Virginia':'KCRW',
|
||||
'Pennsylvania':'KCXY', 'New York':'KALB', 'Vermont':'KMPV',
|
||||
'New Hampshire':'KCON', 'Maine':'KAUG', 'Massachusetts':'KBOS',
|
||||
'Rhode Island':'KPVD', 'Connecticut':'KHFD', 'New Jersey':'KTTN',
|
||||
'Delaware':'KDOV', 'Maryland':'KNAK'}
|
||||
single_value_params = ["timeObs", "stationName", "longitude", "latitude",
|
||||
"temperature", "dewpoint", "windDir",
|
||||
"windSpeed", "seaLevelPress"]
|
||||
multi_value_params = ["presWeather", "skyCover", "skyLayerBase"]
|
||||
all_params = single_value_params + multi_value_params
|
||||
obs_dict = dict({all_params: [] for all_params in all_params})
|
||||
pres_weather = []
|
||||
sky_cov = []
|
||||
sky_layer_base = []
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
from dynamicserialize.dstypes.com.raytheon.uf.common.time import TimeRange
|
||||
from datetime import datetime, timedelta
|
||||
lastHourDateTime = datetime.utcnow() - timedelta(hours = 1)
|
||||
start = lastHourDateTime.strftime('%Y-%m-%d %H')
|
||||
beginRange = datetime.strptime( start + ":00:00", "%Y-%m-%d %H:%M:%S")
|
||||
endRange = datetime.strptime( start + ":59:59", "%Y-%m-%d %H:%M:%S")
|
||||
timerange = TimeRange(beginRange, endRange)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
request.setDatatype("obs")
|
||||
request.setParameters(*(all_params))
|
||||
request.setLocationNames(*(state_capital_wx_stations.values()))
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
response = DataAccessLayer.getGeometryData(request,timerange)
|
||||
for ob in response:
|
||||
avail_params = ob.getParameters()
|
||||
if "presWeather" in avail_params:
|
||||
pres_weather.append(ob.getString("presWeather"))
|
||||
elif "skyCover" in avail_params and "skyLayerBase" in avail_params:
|
||||
sky_cov.append(ob.getString("skyCover"))
|
||||
sky_layer_base.append(ob.getNumber("skyLayerBase"))
|
||||
else:
|
||||
for param in single_value_params:
|
||||
if param in avail_params:
|
||||
if param == 'timeObs':
|
||||
obs_dict[param].append(datetime.fromtimestamp(ob.getNumber(param)/1000.0))
|
||||
else:
|
||||
try:
|
||||
obs_dict[param].append(ob.getNumber(param))
|
||||
except TypeError:
|
||||
obs_dict[param].append(ob.getString(param))
|
||||
else:
|
||||
obs_dict[param].append(None)
|
||||
|
||||
obs_dict['presWeather'].append(pres_weather);
|
||||
obs_dict['skyCover'].append(sky_cov);
|
||||
obs_dict['skyLayerBase'].append(sky_layer_base);
|
||||
pres_weather = []
|
||||
sky_cov = []
|
||||
sky_layer_base = []
|
||||
```
|
||||
|
||||
We can now use pandas to retrieve desired subsets of our observations.
|
||||
|
||||
In this case, return the most recent observation for each station.
|
||||
|
||||
|
||||
```python
|
||||
df = pandas.DataFrame(data=obs_dict, columns=all_params)
|
||||
#sort rows with the newest first
|
||||
df = df.sort_values(by='timeObs', ascending=False)
|
||||
#group rows by station
|
||||
groups = df.groupby('stationName')
|
||||
#create a new DataFrame for the most recent values
|
||||
df_recent = pandas.DataFrame(columns=all_params)
|
||||
#retrieve the first entry for each group, which will
|
||||
#be the most recent observation
|
||||
for rid, station in groups:
|
||||
row = station.head(1)
|
||||
df_recent = pandas.concat([df_recent, row])
|
||||
```
|
||||
|
||||
Convert DataFrame to something metpy-readable by
|
||||
attaching units and calculating derived values
|
||||
|
||||
|
||||
```python
|
||||
data = dict()
|
||||
data['stid'] = np.array(df_recent["stationName"])
|
||||
data['latitude'] = np.array(df_recent['latitude'])
|
||||
data['longitude'] = np.array(df_recent['longitude'])
|
||||
data['air_temperature'] = np.array(df_recent['temperature'], dtype=float)* units.degC
|
||||
data['dew_point'] = np.array(df_recent['dewpoint'], dtype=float)* units.degC
|
||||
data['slp'] = np.array(df_recent['seaLevelPress'])* units('mbar')
|
||||
u, v = get_wind_components(np.array(df_recent['windSpeed']) * units('knots'),
|
||||
np.array(df_recent['windDir']) * units.degree)
|
||||
data['eastward_wind'], data['northward_wind'] = u, v
|
||||
data['cloud_frac'] = [int(get_cloud_cover(x)*8) for x in df_recent['skyCover']]
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
%matplotlib inline
|
||||
import cartopy.crs as ccrs
|
||||
import cartopy.feature as feat
|
||||
from matplotlib import rcParams
|
||||
rcParams['savefig.dpi'] = 100
|
||||
proj = ccrs.LambertConformal(central_longitude=-95, central_latitude=35,
|
||||
standard_parallels=[35])
|
||||
state_boundaries = feat.NaturalEarthFeature(category='cultural',
|
||||
name='admin_1_states_provinces_lines',
|
||||
scale='110m', facecolor='none')
|
||||
# Create the figure
|
||||
fig = plt.figure(figsize=(20, 15))
|
||||
ax = fig.add_subplot(1, 1, 1, projection=proj)
|
||||
|
||||
# Add map elements
|
||||
ax.add_feature(feat.LAND, zorder=-1)
|
||||
ax.add_feature(feat.OCEAN, zorder=-1)
|
||||
ax.add_feature(feat.LAKES, zorder=-1)
|
||||
ax.coastlines(resolution='110m', zorder=2, color='black')
|
||||
ax.add_feature(state_boundaries)
|
||||
ax.add_feature(feat.BORDERS, linewidth='2', edgecolor='black')
|
||||
ax.set_extent((-120, -70, 20, 50))
|
||||
|
||||
# Start the station plot by specifying the axes to draw on, as well as the
|
||||
# lon/lat of the stations (with transform). We also set the fontsize to 12 pt.
|
||||
stationplot = StationPlot(ax, data['longitude'], data['latitude'],
|
||||
transform=ccrs.PlateCarree(), fontsize=12)
|
||||
|
||||
# The layout knows where everything should go, and things are standardized using
|
||||
# the names of variables. So the layout pulls arrays out of `data` and plots them
|
||||
# using `stationplot`.
|
||||
simple_layout.plot(stationplot, data)
|
||||
|
||||
# Plot the temperature and dew point to the upper and lower left, respectively, of
|
||||
# the center point. Each one uses a different color.
|
||||
stationplot.plot_parameter('NW', np.array(data['air_temperature']), color='red')
|
||||
stationplot.plot_parameter('SW', np.array(data['dew_point']), color='darkgreen')
|
||||
|
||||
# A more complex example uses a custom formatter to control how the sea-level pressure
|
||||
# values are plotted. This uses the standard trailing 3-digits of the pressure value
|
||||
# in tenths of millibars.
|
||||
stationplot.plot_parameter('NE', np.array(data['slp']),
|
||||
formatter=lambda v: format(10 * v, '.0f')[-3:])
|
||||
|
||||
# Plot the cloud cover symbols in the center location. This uses the codes made above and
|
||||
# uses the `sky_cover` mapper to convert these values to font codes for the
|
||||
# weather symbol font.
|
||||
stationplot.plot_symbol('C', data['cloud_frac'], sky_cover)
|
||||
|
||||
# Also plot the actual text of the station id. Instead of cardinal directions,
|
||||
# plot further out by specifying a location of 2 increments in x and 0 in y.
|
||||
stationplot.plot_text((2, 0), np.array(obs_dict["stationName"]))
|
||||
|
||||
plt.title("Most Recent Observations for State Capitals")
|
||||
```
|
||||
|
||||

|
136
docs/python/upper-air-bufr-soundings.md
Normal file
|
@ -0,0 +1,136 @@
|
|||
|
||||
The following script takes you through the steps of retrieving an Upper Air vertical profile from an AWIPS EDEX server and plotting a Skew-T/Log-P chart with Matplotlib and MetPy.
|
||||
|
||||
The **bufrua** plugin returns separate objects for parameters at **mandatory levels** and at **significant temperature levels**. For the Skew-T/Log-P plot, significant temperature levels are used to plot the pressure, temperature, and dewpoint lines, while mandatory levels are used to plot the wind profile.
|
||||
|
||||
|
||||
```python
|
||||
%matplotlib inline
|
||||
from awips.dataaccess import DataAccessLayer
|
||||
import matplotlib.tri as mtri
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
||||
import numpy as np
|
||||
import math
|
||||
from metpy.calc import get_wind_speed, get_wind_components, lcl, dry_lapse, parcel_profile
|
||||
from metpy.plots import SkewT, Hodograph
|
||||
from metpy.units import units, concatenate
|
||||
|
||||
# Set host
|
||||
DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
|
||||
request = DataAccessLayer.newDataRequest()
|
||||
|
||||
# Set data type
|
||||
request.setDatatype("bufrua")
|
||||
availableLocs = DataAccessLayer.getAvailableLocationNames(request)
|
||||
availableLocs.sort()
|
||||
|
||||
# Set Mandatory and Significant Temperature level parameters
|
||||
MAN_PARAMS = set(['prMan', 'htMan', 'tpMan', 'tdMan', 'wdMan', 'wsMan'])
|
||||
SIGT_PARAMS = set(['prSigT', 'tpSigT', 'tdSigT'])
|
||||
request.setParameters("wmoStaNum", "validTime", "rptType", "staElev", "numMand",
|
||||
"numSigT", "numSigW", "numTrop", "numMwnd", "staName")
|
||||
request.getParameters().extend(MAN_PARAMS)
|
||||
request.getParameters().extend(SIGT_PARAMS)
|
||||
|
||||
# Set station ID (not name)
|
||||
request.setLocationNames("72562") #KLBF
|
||||
|
||||
# Get all times
|
||||
datatimes = DataAccessLayer.getAvailableTimes(request)
|
||||
|
||||
# Get most recent record
|
||||
response = DataAccessLayer.getGeometryData(request,times=datatimes[-1].validPeriod)
|
||||
|
||||
# Initialize data arrays
|
||||
tdMan,tpMan,prMan,wdMan,wsMan = np.array([]),np.array([]),np.array([]),np.array([]),np.array([])
|
||||
prSig,tpSig,tdSig = np.array([]),np.array([]),np.array([])
|
||||
manGeos = []
|
||||
sigtGeos = []
|
||||
|
||||
# Build arrays
|
||||
for ob in response:
|
||||
if set(ob.getParameters()) & MAN_PARAMS:
|
||||
manGeos.append(ob)
|
||||
prMan = np.append(prMan,ob.getNumber("prMan"))
|
||||
tpMan = np.append(tpMan,ob.getNumber("tpMan"))
|
||||
tdMan = np.append(tdMan,ob.getNumber("tdMan"))
|
||||
wdMan = np.append(wdMan,ob.getNumber("wdMan"))
|
||||
wsMan = np.append(wsMan,ob.getNumber("wsMan"))
|
||||
continue
|
||||
if set(ob.getParameters()) & SIGT_PARAMS:
|
||||
sigtGeos.append(ob)
|
||||
prSig = np.append(prSig,ob.getNumber("prSigT"))
|
||||
tpSig = np.append(tpSig,ob.getNumber("tpSigT"))
|
||||
tdSig = np.append(tdSig,ob.getNumber("tdSigT"))
|
||||
continue
|
||||
|
||||
# Sort mandatory levels (but not sigT levels) because of the 1000.MB interpolation inclusion
|
||||
ps = prMan.argsort()[::-1]
|
||||
wpres = prMan[ps]
|
||||
direc = wdMan[ps]
|
||||
spd = wsMan[ps]
|
||||
tman = tpMan[ps]
|
||||
dman = tdMan[ps]
|
||||
|
||||
# Flag missing data
|
||||
prSig[prSig <= -9999] = np.nan
|
||||
tpSig[tpSig <= -9999] = np.nan
|
||||
tdSig[tdSig <= -9999] = np.nan
|
||||
wpres[wpres <= -9999] = np.nan
|
||||
tman[tman <= -9999] = np.nan
|
||||
dman[dman <= -9999] = np.nan
|
||||
direc[direc <= -9999] = np.nan
|
||||
spd[spd <= -9999] = np.nan
|
||||
|
||||
# assign units
|
||||
p = (prSig/100) * units.mbar
|
||||
T = (tpSig-273.15) * units.degC
|
||||
Td = (tdSig-273.15) * units.degC
|
||||
wpres = (wpres/100) * units.mbar
|
||||
tman = tman * units.degC
|
||||
dman = dman * units.degC
|
||||
u,v = get_wind_components(spd, np.deg2rad(direc))
|
||||
|
||||
# Create SkewT/LogP
|
||||
plt.rcParams['figure.figsize'] = (8, 10)
|
||||
skew = SkewT()
|
||||
skew.plot(p, T, 'r', linewidth=2)
|
||||
skew.plot(p, Td, 'g', linewidth=2)
|
||||
skew.plot_barbs(wpres, u, v)
|
||||
skew.ax.set_ylim(1000, 100)
|
||||
skew.ax.set_xlim(-30, 30)
|
||||
|
||||
title_string = " T(F) Td "
|
||||
title_string += " " + str(ob.getString("staName"))
|
||||
title_string += " " + str(ob.getDataTime().getRefTime())
|
||||
title_string += " (" + str(ob.getNumber("staElev")) + "m elev)"
|
||||
title_string += "\n" + str(round(T[0].to('degF').item(),1))
|
||||
title_string += " " + str(round(Td[0].to('degF').item(),1))
|
||||
plt.title(title_string, loc='left')
|
||||
|
||||
# Calculate LCL height and plot as black dot
|
||||
l = lcl(p[0], T[0], Td[0])
|
||||
lcl_temp = dry_lapse(concatenate((p[0], l)), T[0])[-1].to('degC')
|
||||
skew.plot(l, lcl_temp, 'ko', markerfacecolor='black')
|
||||
|
||||
# Calculate full parcel profile and add to plot as black line
|
||||
prof = parcel_profile(p, T[0], Td[0]).to('degC')
|
||||
skew.plot(p, prof, 'k', linewidth=2)
|
||||
|
||||
# An example of a slanted line at constant T -- in this case the 0 isotherm
|
||||
l = skew.ax.axvline(0, color='c', linestyle='--', linewidth=2)
|
||||
|
||||
# Draw hodograph
|
||||
ax_hod = inset_axes(skew.ax, '30%', '30%', loc=3)
|
||||
h = Hodograph(ax_hod, component_range=max(wsMan))
|
||||
h.add_grid(increment=20)
|
||||
h.plot_colormapped(u, v, spd)
|
||||
|
||||
# Show the plot
|
||||
plt.show()
|
||||
```
|
||||
|
||||
|
||||

|
||||
|